jekyll-mermaid-prebuild 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad9dfff47c169ef73600de5d11407d536e0b16003ad4297e6bab55470aded883
4
- data.tar.gz: 214c1fe4e08e38927c85415aab82eaa542820c07ee5aa4d356e045799d4f34bc
3
+ metadata.gz: f20707d63b254ecd0493fd044b7bb413476f2f5908bfd5e1f8aaeab4a7c4af88
4
+ data.tar.gz: c145bf64bedfb029cdb43f8c513dd302c2f1be8216c42b1c1f17d560244e8f5f
5
5
  SHA512:
6
- metadata.gz: 53beefcd83cc729cc7aea99d55ca7a322b7858c616784f57dc2556ccb4b03ab740643ebe03a72c2a57acf754d197d2d8a911ccacc03b909a408631e4af25feee
7
- data.tar.gz: f9f97e2f5c6539994ff13612c22d06717f817f19cfec5ff471b4dc7cdcd474fe931fa277e6ba5ec4cc93226b1c0c999b7ca16e16a583aac188abc057c4113b1e
6
+ metadata.gz: c076fb8bbb8c812e83a04cd985226e909d9c9edf0f4d49275abfb357a3f66ca2b3db3c5a1dab7486e3bbb29eb5ca0cf8ec61e6d4d617a28203a2dad8b9c7082d
7
+ data.tar.gz: ebe336837f347ed991aeafe75891e6cc4f47005ea8516a344a2e4c55d63144324e866c419db79addf6a2ded2f7749cf762638ec09c9d4fe1f1bef93d7b500709
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.0](https://github.com/Texarkanine/jekyll-mermaid-prebuild/compare/v0.4.0...v0.5.0) (2026-03-22)
4
+
5
+
6
+ ### Features
7
+
8
+ * support "dark mode" (resolves [#11](https://github.com/Texarkanine/jekyll-mermaid-prebuild/issues/11)) ([#20](https://github.com/Texarkanine/jekyll-mermaid-prebuild/issues/20)) ([1ae9322](https://github.com/Texarkanine/jekyll-mermaid-prebuild/commit/1ae93225d9f6089d3d85ce18d06f44310fea3895))
9
+
3
10
  ## [0.4.0](https://github.com/Texarkanine/jekyll-mermaid-prebuild/compare/v0.3.2...v0.4.0) (2026-03-22)
4
11
 
5
12
 
data/README.md CHANGED
@@ -94,6 +94,12 @@ Add to your `_config.yml`:
94
94
  mermaid_prebuild:
95
95
  enabled: true # default: true
96
96
  output_dir: assets/svg # default: assets/svg
97
+ prefers-color-scheme:
98
+ mode: light # light (default) | dark | auto — see [Color scheme](#color-scheme-mermaid-theme)
99
+ # Optional: override mmdc’s root SVG background (defaults: light=white, dark=black)
100
+ # background-color:
101
+ # light: white
102
+ # dark: black
97
103
  postprocessing:
98
104
  text_centering: true # default: true
99
105
  overflow_protection: true # default: true
@@ -108,6 +114,7 @@ mermaid_prebuild:
108
114
  |--------|---------|-------------|
109
115
  | `enabled` | `true` | Enable/disable the plugin |
110
116
  | `output_dir` | `assets/svg` | Directory for generated SVG files |
117
+ | `prefers-color-scheme` | see below | **Hash** with `mode` (`light` / `dark` / `auto`) and optional `background-color` for chart backgrounds. See [Color scheme](#color-scheme-mermaid-theme). |
111
118
 
112
119
  #### `postprocessing` group
113
120
 
@@ -120,6 +127,31 @@ All cross-browser rendering fixes live under the `postprocessing:` key. Each can
120
127
  | `edge_label_padding` | `0` | Extra SVG user units added to edge-label `<foreignObject>` widths after mmdc (off when `0`, `false`, or omitted). Applies to all diagram types. See [Cross-browser text rendering fixes](#cross-browser-text-rendering-fixes). |
121
128
  | `emoji_width_compensation` | `{}` | Map of diagram types to booleans; see [Emoji width compensation](#emoji-width-compensation) below. |
122
129
 
130
+ ### Color scheme (Mermaid theme)
131
+
132
+ Under `mermaid_prebuild`, the **`prefers-color-scheme`** key (hyphenated, like the CSS media feature) must be a **YAML mapping**. Use `mode` for the Mermaid theme / HTML behavior. Optionally nest **`background-color`** (same spelling as in CSS) with `light` / `dark` slots for each chart variant’s root SVG fill (mmdc always emits `background-color: white` on the root `<svg>`; the plugin rewrites that per file).
133
+
134
+ ```yaml
135
+ mermaid_prebuild:
136
+ prefers-color-scheme:
137
+ mode: auto
138
+ background-color:
139
+ light: white # default if omitted
140
+ dark: black # default if omitted
141
+ ```
142
+
143
+ | `mode` | Behavior |
144
+ |--------|----------|
145
+ | `light` | One SVG per diagram using Mermaid’s default (light) theme. |
146
+ | `dark` | One SVG per diagram using mmdc’s dark theme (`mmdc -t dark`). |
147
+ | `auto` | Two SVGs per diagram: `{digest}.svg` (light) and `{digest}-dark.svg` (dark). The embedded HTML uses two links with a small inline stylesheet so only the variant matching the visitor’s `prefers-color-scheme` is shown. **Build cost:** each diagram runs `mmdc` twice until both files are cached. |
148
+
149
+ **Chart backgrounds:** Values are injected verbatim into the root `<svg style="...">` as the CSS token after `background-color:` (for example `white`, `black`, `#fff0aa`, `rgb(0,0,0)`, `transparent`). They are validated conservatively: values containing quotes, angle brackets, backticks, semicolons, backslashes, or control characters are rejected and the default for that slot is used, with a warning. Keep values short (max 256 characters). **Do not** put raw double quotes or HTML in these strings.
150
+
151
+ **Transparency:** The standard CSS keyword **`transparent`** is supported and is the usual choice when you want that variant’s chart to show the page behind it (for example `dark: transparent` on a dark-themed site). Fully transparent RGBA such as `rgba(0, 0, 0, 0)` is also valid if you prefer explicit alpha.
152
+
153
+ If `prefers-color-scheme` is not a hash (for example a bare string), the plugin uses `mode: light` and default backgrounds and logs a warning. Unknown `mode` values fall back to `light` with a warning. Omitting `mode` is treated as `light`.
154
+
123
155
  ### Cross-browser text rendering fixes
124
156
 
125
157
  When mmdc renders a diagram, headless Chromium measures text with `getBoundingClientRect()` and sets each `<foreignObject>` to exactly that width. If the viewing browser (different OS, different fonts) renders the same text at a different width, labels can clip or shift. The plugin can apply some fixes to every generated SVG (all configurable under `postprocessing:`):
@@ -179,7 +211,7 @@ Generated SVGs are cached in `.jekyll-cache/jekyll-mermaid-prebuild/`. The cache
179
211
  - Modified diagrams are automatically regenerated
180
212
  - Different diagrams with different content get different cache keys
181
213
  - Enabling or disabling emoji width compensation for a diagram type invalidates cache for that content (keys include compensated source when applicable)
182
- - Changing `edge_label_padding`, `text_centering`, or `overflow_protection` invalidates cache keys
214
+ - Changing `edge_label_padding`, `text_centering`, `overflow_protection`, `prefers-color-scheme` mode, or chart background colors invalidates cache keys
183
215
 
184
216
  To clear the cache:
185
217
 
@@ -5,8 +5,19 @@ module JekyllMermaidPrebuild
5
5
  class Configuration
6
6
  DEFAULT_OUTPUT_DIR = "assets/svg"
7
7
  CACHE_DIR = ".jekyll-cache/jekyll-mermaid-prebuild"
8
+ DEFAULT_CHART_BG_LIGHT = "white"
9
+ DEFAULT_CHART_BG_DARK = "black"
10
+ # Parsed mode when PCS is omitted, invalid, or empty (not the YAML string "light").
11
+ DEFAULT_PREFERS_COLOR_SCHEME_MODE = :light
12
+ MAX_CHART_BACKGROUND_LENGTH = 256
13
+ INVALID_CHART_BACKGROUND = /[\x00-\x1f"'<>;`\\]/
8
14
 
9
- attr_reader :output_dir, :text_centering, :overflow_protection, :edge_label_padding, :emoji_width_compensation
15
+ # YAML keys under mermaid_prebuild — hyphenated to align with CSS (@media (prefers-color-scheme), background-color).
16
+ PREFERS_COLOR_SCHEME_YAML_KEY = "prefers-color-scheme"
17
+ BACKGROUND_COLOR_YAML_KEY = "background-color"
18
+
19
+ attr_reader :output_dir, :text_centering, :overflow_protection, :edge_label_padding, :emoji_width_compensation,
20
+ :prefers_color_scheme, :chart_background_light, :chart_background_dark
10
21
 
11
22
  # Initialize configuration from Jekyll site
12
23
  #
@@ -15,6 +26,8 @@ module JekyllMermaidPrebuild
15
26
  config = site.config["mermaid_prebuild"] || {}
16
27
  @output_dir = parse_output_dir(config["output_dir"])
17
28
  @enabled = config.fetch("enabled", true)
29
+ pcs_raw = config[PREFERS_COLOR_SCHEME_YAML_KEY]
30
+ parse_prefers_color_scheme(pcs_raw)
18
31
 
19
32
  pp = config["postprocessing"] || {}
20
33
  @text_centering = pp.fetch("text_centering", true)
@@ -39,6 +52,116 @@ module JekyllMermaidPrebuild
39
52
 
40
53
  private
41
54
 
55
+ # Parse the prefers-color-scheme block (see PREFERS_COLOR_SCHEME_YAML_KEY) from a Hash only
56
+ # (mode + optional background-color map). Non-Hash values fall back to :light and default
57
+ # backgrounds with a warning.
58
+ #
59
+ # @param value [Object] raw site config value
60
+ # @return [void]
61
+ def parse_prefers_color_scheme(value)
62
+ unless value.is_a?(Hash)
63
+ @prefers_color_scheme = DEFAULT_PREFERS_COLOR_SCHEME_MODE
64
+ @chart_background_light = finalize_background(DEFAULT_CHART_BG_LIGHT)
65
+ @chart_background_dark = finalize_background(DEFAULT_CHART_BG_DARK)
66
+ unless value.nil?
67
+ Jekyll.logger.warn(
68
+ "MermaidPrebuild:",
69
+ "Invalid #{PREFERS_COLOR_SCHEME_YAML_KEY} (expected a Hash); " \
70
+ "using light mode and default backgrounds"
71
+ )
72
+ end
73
+ return
74
+ end
75
+
76
+ mode_raw = config_hash_fetch(value, "mode")
77
+ @prefers_color_scheme = normalize_prefers_mode(mode_raw)
78
+
79
+ bg_container = config_hash_fetch(value, BACKGROUND_COLOR_YAML_KEY)
80
+ if bg_container.is_a?(Hash)
81
+ light_raw = config_hash_fetch(bg_container, "light")
82
+ dark_raw = config_hash_fetch(bg_container, "dark")
83
+ @chart_background_light = coerce_chart_background(light_raw, DEFAULT_CHART_BG_LIGHT, "light")
84
+ @chart_background_dark = coerce_chart_background(dark_raw, DEFAULT_CHART_BG_DARK, "dark")
85
+ else
86
+ @chart_background_light = finalize_background(DEFAULT_CHART_BG_LIGHT)
87
+ @chart_background_dark = finalize_background(DEFAULT_CHART_BG_DARK)
88
+ end
89
+ end
90
+
91
+ # @param raw [Object]
92
+ # @return [Symbol] :light, :dark, or :auto
93
+ def normalize_prefers_mode(raw)
94
+ return DEFAULT_PREFERS_COLOR_SCHEME_MODE if raw.nil?
95
+
96
+ s = raw.to_s.strip.downcase
97
+ return DEFAULT_PREFERS_COLOR_SCHEME_MODE if s.empty?
98
+
99
+ case s
100
+ when "light" then :light
101
+ when "dark" then :dark
102
+ when "auto" then :auto
103
+ else
104
+ Jekyll.logger.warn(
105
+ "MermaidPrebuild:",
106
+ "Invalid #{PREFERS_COLOR_SCHEME_YAML_KEY} mode #{raw.inspect}; using light"
107
+ )
108
+ DEFAULT_PREFERS_COLOR_SCHEME_MODE
109
+ end
110
+ end
111
+
112
+ # Read a config key from a Hash (string key as in YAML, or matching Symbol).
113
+ #
114
+ # @param hash [Hash]
115
+ # @param key [String] exact key (e.g. "mode", "background-color", "light")
116
+ # @return [Object, nil]
117
+ def config_hash_fetch(hash, key)
118
+ return nil unless hash.is_a?(Hash)
119
+
120
+ return hash[key] if hash.key?(key)
121
+ return hash[key.to_sym] if hash.key?(key.to_sym)
122
+
123
+ nil
124
+ end
125
+
126
+ # @param value [Object] raw color string or nil (use default)
127
+ # @param default [String] fallback literal
128
+ # @param label [String] "light" or "dark" for logging
129
+ # @return [String] frozen sanitized CSS fragment
130
+ def coerce_chart_background(value, default, label)
131
+ return finalize_background(default) if value.nil?
132
+
133
+ str = value.to_s.strip
134
+ if str.empty?
135
+ Jekyll.logger.warn "MermaidPrebuild:",
136
+ "Invalid chart background (#{label}): empty string; using #{default.inspect}"
137
+ return finalize_background(default)
138
+ end
139
+
140
+ if str.length > MAX_CHART_BACKGROUND_LENGTH
141
+ Jekyll.logger.warn "MermaidPrebuild:",
142
+ "Invalid chart background (#{label}): value too long; using #{default.inspect}"
143
+ return finalize_background(default)
144
+ end
145
+
146
+ if chart_background_invalid?(str)
147
+ Jekyll.logger.warn "MermaidPrebuild:",
148
+ "Invalid chart background (#{label}): disallowed characters; using #{default.inspect}"
149
+ return finalize_background(default)
150
+ end
151
+
152
+ str.freeze
153
+ end
154
+
155
+ def chart_background_invalid?(value)
156
+ INVALID_CHART_BACKGROUND.match?(value)
157
+ end
158
+
159
+ # @param value [String]
160
+ # @return [String] frozen copy
161
+ def finalize_background(value)
162
+ value.to_s.freeze
163
+ end
164
+
42
165
  # Returns a frozen Hash of diagram type (string) => boolean. Non-hash values are rejected → {}.
43
166
  #
44
167
  # @param value [Object] raw config value
@@ -19,28 +19,21 @@ module JekyllMermaidPrebuild
19
19
  # @param mermaid_source [String] mermaid diagram definition
20
20
  # @param cache_key [String] digest for caching
21
21
  # @param diagram_type [String, nil] from EmojiCompensator.detect_diagram_type (e.g. "block")
22
- # @return [String, nil] path to cached SVG file or nil on failure
22
+ # @return [Hash{String => String}, nil] stem (cache filename without .svg) => absolute cache path, or nil on failure
23
23
  def generate(mermaid_source, cache_key, diagram_type: nil)
24
- cache_path = File.join(@config.cache_dir, "#{cache_key}.svg")
25
-
26
- # Return cached file if it exists
27
- return cache_path if File.exist?(cache_path)
28
-
29
- # Ensure cache directory exists
30
- FileUtils.mkdir_p(@config.cache_dir)
31
-
32
- # Generate SVG using mmdc
33
- success = MmdcWrapper.render(mermaid_source, cache_path)
34
- return nil unless success
35
-
36
- post_process_svg(cache_path, diagram_type)
37
-
38
- cache_path
24
+ case @config.prefers_color_scheme
25
+ when :light
26
+ generate_one(mermaid_source, cache_key, diagram_type: diagram_type, theme: :default)
27
+ when :dark
28
+ generate_one(mermaid_source, cache_key, diagram_type: diagram_type, theme: :dark)
29
+ when :auto
30
+ generate_auto(mermaid_source, cache_key, diagram_type: diagram_type)
31
+ end
39
32
  end
40
33
 
41
- # Build SVG URL from cache key
34
+ # Build SVG URL from cache key (stem, e.g. digest or digest-dark)
42
35
  #
43
- # @param cache_key [String] the cache key
36
+ # @param cache_key [String] the cache stem
44
37
  # @return [String] URL path to SVG
45
38
  def build_svg_url(cache_key)
46
39
  "/#{@config.output_dir}/#{cache_key}.svg"
@@ -48,9 +41,27 @@ module JekyllMermaidPrebuild
48
41
 
49
42
  # Build figure HTML for a diagram
50
43
  #
51
- # @param svg_url [String] URL to the SVG file
44
+ # @param svg_url [String] URL to the light (or only) SVG file
45
+ # @param dark_url [String, nil] when set (auto mode), second link + CSS toggle for prefers-color-scheme
52
46
  # @return [String] HTML figure element
53
- def build_figure_html(svg_url)
47
+ def build_figure_html(svg_url, dark_url: nil)
48
+ if dark_url
49
+ return <<~HTML
50
+ <figure class="mermaid-diagram">
51
+ <style>
52
+ .mermaid-diagram__light { display: inline; }
53
+ .mermaid-diagram__dark { display: none; }
54
+ @media (prefers-color-scheme: dark) {
55
+ .mermaid-diagram__light { display: none; }
56
+ .mermaid-diagram__dark { display: inline; }
57
+ }
58
+ </style>
59
+ <a class="mermaid-diagram__light" href="#{svg_url}"><img src="#{svg_url}" alt="Mermaid Diagram"></a>
60
+ <a class="mermaid-diagram__dark" href="#{dark_url}"><img src="#{dark_url}" alt="Mermaid Diagram"></a>
61
+ </figure>
62
+ HTML
63
+ end
64
+
54
65
  <<~HTML
55
66
  <figure class="mermaid-diagram">
56
67
  <a href="#{svg_url}"><img src="#{svg_url}" alt="Mermaid Diagram"></a>
@@ -60,12 +71,50 @@ module JekyllMermaidPrebuild
60
71
 
61
72
  private
62
73
 
63
- def post_process_svg(cache_path, _diagram_type)
74
+ # @return [Hash{String => String}, nil]
75
+ def generate_one(mermaid_source, stem, diagram_type:, theme:)
76
+ cache_path = File.join(@config.cache_dir, "#{stem}.svg")
77
+ return { stem => cache_path } if File.exist?(cache_path)
78
+
79
+ FileUtils.mkdir_p(@config.cache_dir)
80
+ return nil unless MmdcWrapper.render(mermaid_source, cache_path, theme: theme)
81
+
82
+ bg = theme == :dark ? @config.chart_background_dark : @config.chart_background_light
83
+ post_process_svg(cache_path, diagram_type, root_background: bg)
84
+ { stem => cache_path }
85
+ end
86
+
87
+ # @return [Hash{String => String}, nil]
88
+ def generate_auto(mermaid_source, cache_key, diagram_type:)
89
+ light_stem = cache_key
90
+ dark_stem = "#{cache_key}-dark"
91
+ light_path = File.join(@config.cache_dir, "#{light_stem}.svg")
92
+ dark_path = File.join(@config.cache_dir, "#{dark_stem}.svg")
93
+
94
+ FileUtils.mkdir_p(@config.cache_dir)
95
+
96
+ unless File.exist?(light_path)
97
+ return nil unless MmdcWrapper.render(mermaid_source, light_path, theme: :default)
98
+
99
+ post_process_svg(light_path, diagram_type, root_background: @config.chart_background_light)
100
+ end
101
+
102
+ unless File.exist?(dark_path)
103
+ return nil unless MmdcWrapper.render(mermaid_source, dark_path, theme: :dark)
104
+
105
+ post_process_svg(dark_path, diagram_type, root_background: @config.chart_background_dark)
106
+ end
107
+
108
+ { light_stem => light_path, dark_stem => dark_path }
109
+ end
110
+
111
+ def post_process_svg(cache_path, _diagram_type, root_background:)
64
112
  raw = File.read(cache_path)
65
113
  svg = raw
66
114
 
67
115
  svg = SvgPostProcessor.ensure_text_centering(svg) if @config.text_centering
68
116
  svg = SvgPostProcessor.ensure_foreignobject_overflow(svg) if @config.overflow_protection
117
+ svg = SvgPostProcessor.apply_root_svg_background(svg, root_background)
69
118
 
70
119
  pad = @config.edge_label_padding
71
120
  svg = SvgPostProcessor.apply(svg, padding: pad) if pad.is_a?(Numeric) && pad.positive?
@@ -95,8 +95,17 @@ module JekyllMermaidPrebuild
95
95
  #
96
96
  # @param mermaid_source [String] mermaid diagram definition
97
97
  # @param output_path [String] path to write SVG file
98
+ ALLOWED_RENDER_THEMES = %i[default dark].freeze
99
+
100
+ # @param theme [Symbol] :default (mermaid default theme) or :dark (mmdc -t dark)
98
101
  # @return [Boolean] true if successful
99
- def render(mermaid_source, output_path)
102
+ # @raise [ArgumentError] if theme is not supported
103
+ def render(mermaid_source, output_path, theme: :default)
104
+ unless ALLOWED_RENDER_THEMES.include?(theme)
105
+ raise ArgumentError,
106
+ "unsupported mmdc theme #{theme.inspect} (allowed: #{ALLOWED_RENDER_THEMES.map(&:inspect).join(", ")})"
107
+ end
108
+
100
109
  input_file = Tempfile.new(["mermaid", ".mmd"])
101
110
 
102
111
  begin
@@ -104,6 +113,7 @@ module JekyllMermaidPrebuild
104
113
  input_file.close
105
114
 
106
115
  cmd = ["mmdc", "-i", input_file.path, "-o", output_path, "-e", "svg"]
116
+ cmd += ["-t", "dark"] if theme == :dark
107
117
  _stdout, _stderr, status = Open3.capture3(*cmd)
108
118
 
109
119
  status.success?
@@ -37,7 +37,7 @@ module JekyllMermaidPrebuild
37
37
  next unless result
38
38
 
39
39
  converted_count += 1
40
- svgs_to_copy[result[:cache_key]] = result[:cached_path]
40
+ svgs_to_copy.merge!(result[:svgs])
41
41
  processed[block[:start]...block[:end]] = result[:html]
42
42
  end
43
43
 
@@ -51,6 +51,9 @@ module JekyllMermaidPrebuild
51
51
  # @return [String] input to MD5 for cache key
52
52
  def digest_string_for_cache(source, _diagram_type)
53
53
  parts = [source]
54
+ parts << "pcs=#{@config.prefers_color_scheme}"
55
+ parts << "bgL=#{@config.chart_background_light}"
56
+ parts << "bgD=#{@config.chart_background_dark}"
54
57
  parts << "tc=#{@config.text_centering}" unless @config.text_centering
55
58
  parts << "op=#{@config.overflow_protection}" unless @config.overflow_protection
56
59
  pad = @config.edge_label_padding
@@ -61,7 +64,7 @@ module JekyllMermaidPrebuild
61
64
  # Convert a single mermaid block to SVG
62
65
  #
63
66
  # @param block [Hash] block info with :content key
64
- # @return [Hash, nil] {cache_key:, cached_path:, html:} or nil if failed
67
+ # @return [Hash, nil] {:svgs, :html} or nil if failed
65
68
  def convert_block(block)
66
69
  mermaid_source = block[:content]
67
70
  diagram_type = EmojiCompensator.detect_diagram_type(mermaid_source)
@@ -72,12 +75,19 @@ module JekyllMermaidPrebuild
72
75
  end
73
76
  digest_input = digest_string_for_cache(source_for_render, diagram_type)
74
77
  cache_key = DigestCalculator.content_digest(digest_input)
75
- cached_path = @generator.generate(source_for_render, cache_key, diagram_type: diagram_type)
78
+ paths = @generator.generate(source_for_render, cache_key, diagram_type: diagram_type)
76
79
 
77
- return nil unless cached_path
80
+ return nil if paths.nil? || paths.empty?
78
81
 
79
- svg_url = @generator.build_svg_url(cache_key)
80
- { cache_key: cache_key, cached_path: cached_path, html: @generator.build_figure_html(svg_url) }
82
+ light_url = @generator.build_svg_url(cache_key)
83
+ html = if @config.prefers_color_scheme == :auto
84
+ dark_url = @generator.build_svg_url("#{cache_key}-dark")
85
+ @generator.build_figure_html(light_url, dark_url: dark_url)
86
+ else
87
+ @generator.build_figure_html(light_url)
88
+ end
89
+
90
+ { svgs: paths, html: html }
81
91
  end
82
92
 
83
93
  # Find all top-level mermaid code blocks (not nested inside other fences)
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JekyllMermaidPrebuild
4
- # Post-processes mmdc-generated SVGs to fix cross-browser rendering issues.
4
+ # Post-processes mmdc-generated SVGs to fix rendering issues.
5
5
  #
6
- # Three independent fixes:
6
+ # Four independent fixes:
7
7
  # 1. Text centering: Mermaid's CSS `text-align: center` targets SVG `<g>` elements where it
8
8
  # has no effect on HTML inside `<foreignObject>`. We inject a CSS rule so that foreignObject
9
9
  # content centers correctly regardless of text measurement differences between the generating
@@ -14,6 +14,9 @@ module JekyllMermaidPrebuild
14
14
  # 3. Edge label padding: Widens edge-label `<foreignObject>` widths in any diagram type to
15
15
  # prevent clipping when the viewing browser renders text wider than headless Chromium measured.
16
16
  # Opt-in via `postprocessing.edge_label_padding` config.
17
+ # 4. Root SVG background: mmdc always emits `background-color: white` on the root `<svg>` regardless
18
+ # of theme. The plugin replaces that token with configurable CSS color values for light and
19
+ # dark variants (defaults white / black) so charts match page background in both modes.
17
20
  module SvgPostProcessor
18
21
  module_function
19
22
 
@@ -75,6 +78,25 @@ module JekyllMermaidPrebuild
75
78
  svg_string
76
79
  end
77
80
 
81
+ # Replace `background-color: white` on the root <svg> style attribute with a caller-supplied
82
+ # CSS color (already sanitized by Configuration). Idempotent when mmdc output no longer contains
83
+ # the white token or the value already matches.
84
+ #
85
+ # @param svg_string [String] full SVG document from mmdc
86
+ # @param css_background [String] literal after `background-color:` (e.g. "black", "#fff0aa")
87
+ # @return [String] SVG with updated root background, or original on no-op / error
88
+ def apply_root_svg_background(svg_string, css_background)
89
+ return svg_string unless svg_string.is_a?(String)
90
+ return svg_string unless css_background.is_a?(String) && !css_background.empty?
91
+
92
+ svg_string.sub(
93
+ /(<svg\b[^>]*\bstyle="[^"]*?)background-color:\s*white;?/,
94
+ "\\1background-color: #{css_background};"
95
+ )
96
+ rescue StandardError
97
+ svg_string
98
+ end
99
+
78
100
  def apply_edge_label_padding(svg_string, padding)
79
101
  svg_string.gsub(EDGE_LABEL_FOREIGN_OBJECT_RE) do
80
102
  prefix = Regexp.last_match(1)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JekyllMermaidPrebuild
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-mermaid-prebuild
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Texarkanine