jekyll-imgflow 0.4.0 → 0.4.2

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: 8400a4bc3d213b557802452b3af0816098443aa5542f27e81bf690dd2ee3f7a8
4
- data.tar.gz: 1f8562de9d69d7451603623ab2e63741f4889a4d04d35d9ae01a561878e8d539
3
+ metadata.gz: c30d610c031609c2e1e9040c115d7162013db83ee6149d76bdb87bc97ad7137c
4
+ data.tar.gz: 8c9f212eb074af380df14d2f168f9d9379db196c8ec9b1a4111a850f804015e6
5
5
  SHA512:
6
- metadata.gz: cbd0f3c0d194bd7acfdc199c9c1eb78dcb69d09c4cc9f213ceb48d1c1c5cb4402518a87a14f44f234df1487ae6eba4495f92aae2b31bb602b199bcaaab4438ef
7
- data.tar.gz: 7c07124101bf734ebedce868d1329900c79acde6d90463d5fbd680fe49f02517e743ba4d7d37ebf3e26454e3e9dd4df80ada8265874bb96b1659767a33a8306f
6
+ metadata.gz: a7f9640c4e7332e769481922c2569114baccc396b6ec4c354f21292421c489ce2d98a3ceaabe50043e10cb351ec497dd88069fb8efe3c3af162774d5148d0adc
7
+ data.tar.gz: a4d19058e5c05578d51a3cb96e6ccf84e84c0516b35cb26ec8c49e7ae18314bdc04725726976d49cdfe9e7e5507be2a18ecc311b66d956e55913ea60eb9f0235
data/README.md CHANGED
@@ -4,10 +4,13 @@
4
4
 
5
5
  A [Jekyll](https://jekyllrb.com/) plugin for automatic image optimization with multiple providers and formats.
6
6
 
7
- [![DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/gundestrup/jekyll-imgflow)
7
+ [![CI](https://github.com/gundestrup/jekyll-imgflow/actions/workflows/ci.yml/badge.svg)](https://github.com/gundestrup/jekyll-imgflow/actions/workflows/ci.yml)
8
+ [![Gem Version](https://img.shields.io/gem/v/jekyll-imgflow)](https://rubygems.org/gems/jekyll-imgflow)
9
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/gundestrup/jekyll-imgflow)
8
10
  [![Status](https://img.shields.io/badge/status-active-success)](https://github.com/gundestrup/jekyll-imgflow)
9
11
  [![Semgrep](https://img.shields.io/badge/Semgrep-scanned-1B2B34?logo=semgrep&logoColor=white)](https://semgrep.dev/)
10
12
  [![CodeFactor](https://www.codefactor.io/repository/github/gundestrup/jekyll-imgflow/badge)](https://www.codefactor.io/repository/github/gundestrup/jekyll-imgflow)
13
+ [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=gundestrup_jekyll-imgflow&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=gundestrup_jekyll-imgflow)
11
14
  [![codecov](https://codecov.io/gh/gundestrup/jekyll-imgflow/branch/main/graph/badge.svg)](https://codecov.io/gh/gundestrup/jekyll-imgflow)
12
15
  [![VS Code Extension](https://img.shields.io/badge/VS%20Code%20Companion-jekyll--imgflow-blue?logo=visual-studio-code)](https://github.com/gundestrup/jekyll-imgflow-vscode)
13
16
  [![License](https://img.shields.io/github/license/gundestrup/jekyll-imgflow)](LICENSE)
@@ -34,7 +37,7 @@ imgflow:
34
37
  > **Configuration:** Only `originals` and `output` are required in `_config.yml`
35
38
  > — everything else has sensible defaults. See the
36
39
  > [Installation Guide](docs/installation.md#4-add-to-_configyml) for full options.
37
-
40
+ >
38
41
  > **Note:** Image references require exact filenames (or paths relative to
39
42
  > `originals`). There is no fuzzy matching during the build yet — if you type
40
43
  > `photo.jpg` but the file is `photo.jpeg`, the build will fail with an error.
@@ -54,21 +57,26 @@ imgflow:
54
57
  ## 📚 Documentation
55
58
 
56
59
  ### Getting Started
60
+
57
61
  - **[Installation Guide](docs/installation.md)** - Detailed setup instructions
58
62
  - **[Basic Usage](docs/usage/)** - Core functionality and examples
63
+ - **[Tag Options](docs/usage/tag-options.md)** - Every `{% imgflow %}` parameter
59
64
  - **[Picture Tag Migration](docs/picture_tag_migration.md)** - Migrate from Picture Tag
60
65
 
61
66
  ### Configuration
67
+
62
68
  - **[Provider Setup](docs/providers.md)** - Configure image providers
63
69
  - **[Docker Services](docs/docker.md)** - Using Docker containers
64
70
  - **[Presets System](docs/usage/presets.md)** - Predefined configurations
65
71
 
66
72
  ### Development
73
+
67
74
  - **[Development Guide](docs/development.md)** - Contributing and setup
68
75
  - **[Testing Guide](docs/testing.md)** - Running and writing tests
69
76
  - **[Scripts Reference](docs/scripts.md)** - Development utilities
70
77
 
71
78
  ### Performance
79
+
72
80
  - **[Provider Comparison](docs/providers.md)** - Performance benchmarks
73
81
  - **[Parallel Testing](docs/parallel_testing.md)** - Fast test execution
74
82
 
@@ -79,6 +87,7 @@ ImgFlow has two processing flows: **Build-Time** (pre-generation) and **Runtime*
79
87
  **See:** [ARCHITECTURE.md](docs/ARCHITECTURE.md) for detailed component architecture and data flow
80
88
 
81
89
  ### Quick Overview
90
+
82
91
  - **Build-Time Flow** - Pre-generate images during Jekyll build
83
92
  - **Runtime Flow** - Process images on-demand during template rendering
84
93
  - **Providers** - Image processing backends (CLI tools, HTTP APIs)
@@ -87,11 +96,13 @@ ImgFlow has two processing flows: **Build-Time** (pre-generation) and **Runtime*
87
96
  ## 🎯 Provider Support
88
97
 
89
98
  ### HTTP API Services (Recommended)
99
+
90
100
  - **[Imgproxy](https://github.com/imgproxy/imgproxy)** - Fast, reliable (Port 4022)
91
101
  - **[Weserv](https://github.com/weserv/images)** - Battle-tested (Port 4026)
92
102
  - **[Flyimg](https://github.com/flyimg/flyimg)** - On-the-fly processing (Port 4030)
93
103
 
94
104
  ### CLI Tools (Local)
105
+
95
106
  - **[Sharp](https://github.com/lovell/sharp)** - Node.js/libvips processing
96
107
  - **[ImageMagick](https://github.com/ImageMagick/ImageMagick)** - Feature-rich
97
108
  - **[LibVips](https://github.com/libvips/libvips)** - Memory efficient
@@ -142,6 +153,7 @@ rake check_services # Verify services
142
153
  ## 📦 Installation
143
154
 
144
155
  ### Quick Setup
156
+
145
157
  ```bash
146
158
  # Add to Gemfile
147
159
  gem 'jekyll-imgflow'
@@ -166,6 +178,8 @@ imgflow:
166
178
 
167
179
  **See:** [installation.md](docs/installation.md) for detailed installation and
168
180
  [all configuration options](docs/installation.md#4-add-to-_configyml),
181
+ [tag-options.md](docs/usage/tag-options.md) for every `{% imgflow %}` option
182
+ (operations, HTML attributes, `markup:` output formats),
169
183
  [presets.md](docs/usage/presets.md) for the preset system
170
184
 
171
185
  ## 🐳 Docker Setup (Recommended)
@@ -205,7 +219,7 @@ bin/imgflow_migrate_presets
205
219
  Latest benchmarks show **Sharp** as the fastest provider:
206
220
 
207
221
  | Provider | Time (s) | Speed |
208
- |----------|----------|-------|
222
+ | ---------- | ---------- | ------- |
209
223
  | [Sharp](https://github.com/lovell/sharp) | 13.97 | 🏆 Fastest |
210
224
  | [LibVips](https://github.com/libvips/libvips) | 21.89 | Very Fast |
211
225
  | [Weserv](https://github.com/weserv/images) | 30.19 | Fast |
@@ -217,6 +231,7 @@ Latest benchmarks show **Sharp** as the fastest provider:
217
231
  ## 🛠️ Development
218
232
 
219
233
  ### Quick Setup
234
+
220
235
  ```bash
221
236
  # Clone and setup
222
237
  git clone <repository>
@@ -230,7 +245,7 @@ rake download_test_images
230
245
  rake ci
231
246
 
232
247
  # Run the local Semgrep Pro rules directly
233
- semgrep scan --pro --config .semgrep.yml --error lib/
248
+ semgrep scan --pro --config .semgrep.yml --error .
234
249
 
235
250
  # Sync the repository scan and findings with the Semgrep dashboard
236
251
  # (requires semgrep login or SEMGREP_APP_TOKEN)
@@ -238,6 +253,7 @@ semgrep ci
238
253
  ```
239
254
 
240
255
  ### Key Files
256
+
241
257
  - **`lib/jekyll-imgflow.rb`** - Main module
242
258
  - **`lib/jekyll-imgflow/imgflow_tag.rb`** - Main tag implementation
243
259
  - **`lib/jekyll-imgflow/html_generator.rb`** - HTML generation
data/interface.yml ADDED
@@ -0,0 +1,63 @@
1
+ # Generated by `rake interface` — do not edit.
2
+ # Public tag/config surface for tooling (editor extensions, doc linters).
3
+ ---
4
+ gem: jekyll-imgflow
5
+ version: 0.4.2
6
+ tags:
7
+ imgflow:
8
+ params:
9
+ - alt
10
+ - aspect_ratio
11
+ - class
12
+ - format
13
+ - formats
14
+ - height
15
+ - keep
16
+ - level
17
+ - link
18
+ - loading
19
+ - markup
20
+ - modal
21
+ - opacity
22
+ - optimize
23
+ - position
24
+ - preset
25
+ - quality
26
+ - ratio
27
+ - title
28
+ - watermark
29
+ - width
30
+ filters: []
31
+ config:
32
+ imgflow:
33
+ - backend_priority
34
+ - cache_dir
35
+ - fallback_format
36
+ - flyimg_url
37
+ - formats
38
+ - image_modal
39
+ - imgproxy_url
40
+ - input_formats
41
+ - optimize_qualities
42
+ - originals
43
+ - output
44
+ - quality
45
+ - sharp_url
46
+ - sizes
47
+ - weserv_url
48
+ enums:
49
+ markup:
50
+ - auto
51
+ - data_auto
52
+ - data_img
53
+ - data_picture
54
+ - direct_url
55
+ - img
56
+ - naked_srcset
57
+ - picture
58
+ level:
59
+ - default
60
+ - high
61
+ - low
62
+ - maximum
63
+ - medium
@@ -44,35 +44,11 @@ module JekyllImgFlow
44
44
  original_paths = find_original_images
45
45
  Jekyll.logger.info "📸 Found #{original_paths.length} original images"
46
46
 
47
- originals_dir = File.join(@site.source, @config.originals)
48
-
49
47
  # Clean up manifest entries for deleted originals
50
- current_originals = original_paths.map { |path| path.sub("#{originals_dir}/", "") }
51
- @manifest.cleanup_deleted_originals(current_originals)
52
- @manifest.cleanup_obsolete_defaults(expected_default_operations)
53
- @manifest.reset_page_usage unless incremental_build?
48
+ prepare_manifest(original_paths)
54
49
 
55
50
  # Build tasks for each original image
56
- original_paths.reject { |path| AnimatedGifDetector.animated?(path) }.each do |path|
57
- original_name = path.sub("#{originals_dir}/", "")
58
- file_digest = @filename_generator.file_digest(path)
59
-
60
- # Check if needs processing (file changed or provider changed)
61
- if needs_processing?(original_name, path, file_digest)
62
- Jekyll.logger.info "🔧 Queuing default versions for: #{original_name}"
63
-
64
- # Build default tasks
65
- tasks = BatchManager.build_default_tasks(
66
- original_name, path, @config, @site, file_digest: file_digest
67
- )
68
- force_processing = source_digest_changed?(original_name, file_digest)
69
- tasks.each { |task| task[:force_processing] = force_processing }
70
- @batch_manager.add_tasks(tasks)
71
- else
72
- expected_default_operations.length.times { @operation_processor.stats.record_cache_hit }
73
- Jekyll.logger.debug "⏭️ Skipping #{original_name} - already up-to-date"
74
- end
75
- end
51
+ queue_default_tasks(original_paths)
76
52
 
77
53
  # Process all queued tasks
78
54
  results = @batch_manager.process_all
@@ -89,6 +65,39 @@ module JekyllImgFlow
89
65
 
90
66
  private
91
67
 
68
+ def prepare_manifest(original_paths)
69
+ originals_dir = File.join(@site.source, @config.originals)
70
+ current_originals = original_paths.map { |path| path.sub("#{originals_dir}/", "") }
71
+ @manifest.cleanup_deleted_originals(current_originals)
72
+ @manifest.cleanup_obsolete_defaults(expected_default_operations)
73
+ @manifest.reset_page_usage unless incremental_build?
74
+ end
75
+
76
+ def queue_default_tasks(original_paths)
77
+ originals_dir = File.join(@site.source, @config.originals)
78
+ original_paths.reject { |path| AnimatedGifDetector.animated?(path) }.each do |path|
79
+ queue_tasks_for(path, path.sub("#{originals_dir}/", ""))
80
+ end
81
+ end
82
+
83
+ def queue_tasks_for(path, original_name)
84
+ file_digest = @filename_generator.file_digest(path)
85
+ unless needs_processing?(original_name, path, file_digest)
86
+ expected_default_operations.length.times { @operation_processor.stats.record_cache_hit }
87
+ Jekyll.logger.debug "⏭️ Skipping #{original_name} - already up-to-date"
88
+ return
89
+ end
90
+
91
+ Jekyll.logger.info "🔧 Queuing default versions for: #{original_name}"
92
+
93
+ tasks = BatchManager.build_default_tasks(
94
+ original_name, path, @config, @site, file_digest: file_digest
95
+ )
96
+ force_processing = source_digest_changed?(original_name, file_digest)
97
+ tasks.each { |task| task[:force_processing] = force_processing }
98
+ @batch_manager.add_tasks(tasks)
99
+ end
100
+
92
101
  def register_skipped_tasks(completed_tasks)
93
102
  provider_name = @registry.current_provider&.class&.provider_name || "unknown"
94
103
  completed_tasks.each do |completed|
@@ -127,13 +136,17 @@ module JekyllImgFlow
127
136
  current_provider = @registry.current_provider&.class&.provider_name || "unknown"
128
137
 
129
138
  expected_default_operations.any? do |operations|
130
- version = default_versions.find do |candidate|
131
- @manifest.same_operations?(candidate["operations"], operations)
132
- end
139
+ version = find_default_version(default_versions, operations)
133
140
  !current_default_version?(version, current_provider, file_digest)
134
141
  end
135
142
  end
136
143
 
144
+ def find_default_version(default_versions, operations)
145
+ default_versions.find do |candidate|
146
+ @manifest.same_operations?(candidate["operations"], operations)
147
+ end
148
+ end
149
+
137
150
  def expected_default_operations
138
151
  @config.sizes.each_value.flat_map do |width|
139
152
  @config.formats.map do |format|
@@ -22,6 +22,18 @@ module JekyllImgFlow
22
22
  DEFAULT_FALLBACK_FORMAT = "jpg"
23
23
  DEFAULT_IMAGE_MODAL = true
24
24
 
25
+ # Core config keys mapped to their defaults (assigned as @ivar per key).
26
+ CORE_CONFIG_DEFAULTS = {
27
+ "originals" => DEFAULT_ORIGINALS,
28
+ "output" => DEFAULT_OUTPUT,
29
+ "input_formats" => DEFAULT_INPUT_FORMATS,
30
+ "sizes" => DEFAULT_SIZES,
31
+ "formats" => DEFAULT_FORMATS,
32
+ "quality" => DEFAULT_QUALITY,
33
+ "backend_priority" => DEFAULT_BACKEND_PRIORITY,
34
+ "fallback_format" => DEFAULT_FALLBACK_FORMAT
35
+ }.freeze
36
+
25
37
  attr_reader :site, :originals, :output, :input_formats, :sizes, :formats,
26
38
  :quality, :backend_priority, :imgproxy_url,
27
39
  :weserv_url, :flyimg_url,
@@ -37,14 +49,9 @@ module JekyllImgFlow
37
49
  end
38
50
 
39
51
  def assign_core_config(cfg)
40
- @originals = cfg["originals"] || DEFAULT_ORIGINALS
41
- @output = cfg["output"] || DEFAULT_OUTPUT
42
- @input_formats = cfg["input_formats"] || DEFAULT_INPUT_FORMATS
43
- @sizes = cfg["sizes"] || DEFAULT_SIZES
44
- @formats = cfg["formats"] || DEFAULT_FORMATS
45
- @quality = cfg["quality"] || DEFAULT_QUALITY
46
- @backend_priority = cfg["backend_priority"] || DEFAULT_BACKEND_PRIORITY
47
- @fallback_format = cfg["fallback_format"] || DEFAULT_FALLBACK_FORMAT
52
+ CORE_CONFIG_DEFAULTS.each do |key, default|
53
+ instance_variable_set("@#{key}", cfg[key] || default)
54
+ end
48
55
  @image_modal = cfg.fetch("image_modal", DEFAULT_IMAGE_MODAL)
49
56
  end
50
57
 
@@ -33,7 +33,7 @@ module JekyllImgFlow
33
33
  end
34
34
 
35
35
  # Save final manifest after template rendering (single writer)
36
- Jekyll::Hooks.register :site, :post_write do |site|
36
+ Jekyll::Hooks.register :site, :post_write, priority: :normal do |site|
37
37
  Jekyll.logger.info "🔌 ImgFlow post_write hook - saving final manifest with page usage"
38
38
 
39
39
  if site.imgflow_components && site.imgflow_components[:manifest]
@@ -6,6 +6,11 @@ module JekyllImgFlow
6
6
  class HtmlGenerator
7
7
  include ModalWrapper
8
8
 
9
+ # Markup formats accepted via `markup:` (see docs/usage/tag-options.md).
10
+ # Unknown values fall back to `img`.
11
+ MARKUP_FORMATS = %w[img picture auto data_img data_picture data_auto
12
+ direct_url naked_srcset].freeze
13
+
9
14
  # Generate HTML based on markup format and attributes
10
15
  # @param results [Array<String>] Processed image paths
11
16
  # @param attributes [Hash] HTML attributes by element
@@ -113,7 +113,8 @@ module Jekyll
113
113
  relative_modal_results = modal_results.uniq.map do |result|
114
114
  relative_result_path(result, site)
115
115
  end
116
- parsed = parsed.merge(markup_format: "picture") if relative_results.length > 1
116
+ auto_markup = [nil, "auto"].include?(parsed[:markup_format])
117
+ parsed = parsed.merge(markup_format: "picture") if relative_results.length > 1 && auto_markup
117
118
  generate_html(relative_results, parsed.merge(modal_results: relative_modal_results), context)
118
119
  end
119
120
 
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JekyllImgFlow
4
+ # Machine-readable description of the plugin's public interface:
5
+ # Liquid tags + their params, config keys, and enum values.
6
+ # `rake interface` writes this as interface.yml (shipped in the gem) so
7
+ # tooling like editor extensions can consume it without parsing Ruby.
8
+ module Interface
9
+ TAGS = {
10
+ "imgflow" => (Parser::OPERATION_PARAMS + Parser::HTML_ATTRIBUTES + %i[markup])
11
+ .map(&:to_s).sort.freeze
12
+ }.freeze
13
+
14
+ # Config keys read outside CORE_CONFIG_DEFAULTS in config.rb
15
+ EXTRA_CONFIG_KEYS = %w[
16
+ cache_dir image_modal imgproxy_url weserv_url flyimg_url sharp_url
17
+ optimize_qualities
18
+ ].freeze
19
+
20
+ def self.to_h
21
+ {
22
+ "gem" => "jekyll-imgflow",
23
+ "version" => VERSION,
24
+ "tags" => TAGS.transform_values { |params| { "params" => params } },
25
+ "filters" => [],
26
+ "config" => {
27
+ "imgflow" => (Config::CORE_CONFIG_DEFAULTS.keys + EXTRA_CONFIG_KEYS).sort
28
+ },
29
+ "enums" => {
30
+ "markup" => HtmlGenerator::MARKUP_FORMATS.sort,
31
+ "level" => %w[default high low maximum medium]
32
+ }
33
+ }
34
+ end
35
+ end
36
+ end
@@ -135,17 +135,23 @@ module JekyllImgFlow
135
135
 
136
136
  max_width = @config.sizes.values.max
137
137
  Dir.children(directory).filter_map do |filename|
138
- candidate = JekyllImgFlow::FilenameGenerator.new.parse_filename(filename)
139
- next unless candidate[:base_name] == parsed[:base_name]
140
- next unless candidate[:hash] == parsed[:hash]
141
- next if max_width && candidate[:width] > max_width
142
-
143
- File.join(File.dirname(result), filename)
138
+ matching_variant_path(filename, parsed, max_width, result)
144
139
  end
145
140
  rescue Errno::ENOENT
146
141
  []
147
142
  end
148
143
 
144
+ # Return the output path for +filename+ when it is a variant of the same
145
+ # image (same base name and content hash) within the max configured width.
146
+ def matching_variant_path(filename, parsed, max_width, result)
147
+ candidate = JekyllImgFlow::FilenameGenerator.new.parse_filename(filename)
148
+ return unless candidate[:base_name] == parsed[:base_name]
149
+ return unless candidate[:hash] == parsed[:hash]
150
+ return if max_width && candidate[:width] > max_width
151
+
152
+ File.join(File.dirname(result), filename)
153
+ end
154
+
149
155
  def modal_width(result)
150
156
  JekyllImgFlow::FilenameGenerator.new.parse_filename(File.basename(result))[:width] || 0
151
157
  end
@@ -185,6 +185,7 @@ module JekyllImgFlow
185
185
  operations: [],
186
186
  html_attributes: {},
187
187
  raw_options: raw_options,
188
+ markup_format: nil,
188
189
  preset: nil,
189
190
  error: error_message
190
191
  }
@@ -196,6 +197,7 @@ module JekyllImgFlow
196
197
  operations: operations,
197
198
  html_attributes: html_attributes,
198
199
  raw_options: raw_options,
200
+ markup_format: raw_options[:markup] || raw_options[:markup_format],
199
201
  preset: raw_options[:preset]
200
202
  }
201
203
  end
@@ -147,28 +147,34 @@ module JekyllImgFlow
147
147
 
148
148
  def merge_operation_tags(tags, op_type, params)
149
149
  case op_type
150
- when "resize"
151
- tags[:width] = params["width"] if params["width"]
152
- tags[:height] = params["height"] if params["height"]
153
- when "crop"
154
- tags[:ratio] = params["ratio"] if params["ratio"]
155
- tags[:aspect_ratio] = params["aspect_ratio"] if params["aspect_ratio"]
156
- tags[:width] = params["width"] if params["width"] && !tags[:width]
157
- tags[:height] = params["height"] if params["height"] && !tags[:height]
158
- when "format"
159
- merge_format_tag(tags, params)
160
- when "quality"
161
- tags[:quality] = params["quality"] if params["quality"]
162
- when "optimize"
163
- tags[:optimize] = true
164
- tags[:level] = params["level"] if params["level"]
165
- when "opacity"
166
- tags[:opacity] = params["opacity"] if params["opacity"]
150
+ when "resize" then merge_resize_tags(tags, params)
151
+ when "crop" then merge_crop_tags(tags, params)
152
+ when "format" then merge_format_tag(tags, params)
153
+ when "quality" then tags[:quality] = params["quality"] if params["quality"]
154
+ when "optimize" then merge_optimize_tags(tags, params)
155
+ when "opacity" then tags[:opacity] = params["opacity"] if params["opacity"]
167
156
  else
168
157
  merge_generic_tags(tags, params)
169
158
  end
170
159
  end
171
160
 
161
+ def merge_resize_tags(tags, params)
162
+ tags[:width] = params["width"] if params["width"]
163
+ tags[:height] = params["height"] if params["height"]
164
+ end
165
+
166
+ def merge_crop_tags(tags, params)
167
+ tags[:ratio] = params["ratio"] if params["ratio"]
168
+ tags[:aspect_ratio] = params["aspect_ratio"] if params["aspect_ratio"]
169
+ tags[:width] = params["width"] if params["width"] && !tags[:width]
170
+ tags[:height] = params["height"] if params["height"] && !tags[:height]
171
+ end
172
+
173
+ def merge_optimize_tags(tags, params)
174
+ tags[:optimize] = true
175
+ tags[:level] = params["level"] if params["level"]
176
+ end
177
+
172
178
  def merge_format_tag(tags, params)
173
179
  if params["formats"]
174
180
  tag_value = params["formats"].is_a?(Array) ? params["formats"].join(",") : params["formats"]
@@ -45,14 +45,19 @@ module JekyllImgFlow
45
45
  params = operation[:params] || {}
46
46
  keep = opts[:keep] || params[:keep] || params[:position]
47
47
  smartcrop = operation[:ratio] && keep && SMARTCROP_POSITIONS.include?(keep.to_s)
48
- coords = if operation[:ratio]
49
- { x: opts[:calculated_x], y: opts[:calculated_y],
50
- width: opts[:calculated_width], height: opts[:calculated_height] }
51
- else
52
- { x: opts[:x] || 0, y: opts[:y] || 0,
53
- width: opts[:width], height: opts[:height] }
54
- end
55
- coords.merge(smartcrop: smartcrop, keep: keep)
48
+ crop_coords(operation, opts).merge(smartcrop: smartcrop, keep: keep)
49
+ end
50
+
51
+ # Coordinates for a crop operation: ratio crops use calculated values,
52
+ # explicit crops use x/y/width/height options (defaulting x/y to 0).
53
+ def crop_coords(operation, opts)
54
+ if operation[:ratio]
55
+ { x: opts[:calculated_x], y: opts[:calculated_y],
56
+ width: opts[:calculated_width], height: opts[:calculated_height] }
57
+ else
58
+ { x: opts[:x] || 0, y: opts[:y] || 0,
59
+ width: opts[:width], height: opts[:height] }
60
+ end
56
61
  end
57
62
 
58
63
  # Extract watermark operation parts into a hash.
@@ -145,17 +150,7 @@ module JekyllImgFlow
145
150
 
146
151
  # For HTTP providers, construct proper HTTP URL
147
152
  site = @config.site
148
-
149
- # Get the relative path from site source using Pathname
150
- source_path = Pathname.new(site.source)
151
- file_pathname = Pathname.new(file_path)
152
- relative_path = if file_pathname.absolute? && file_path.to_s.start_with?(site.source)
153
- file_pathname.relative_path_from(source_path).to_s
154
- elsif file_path.start_with?("/")
155
- file_path[1..]
156
- else
157
- file_path
158
- end
153
+ relative_path = relative_file_path(file_path, site)
159
154
 
160
155
  # URL-encode each path segment to handle spaces and special characters
161
156
  encoded_path = relative_path.split("/").map do |segment|
@@ -171,6 +166,18 @@ module JekyllImgFlow
171
166
  "#{base_url}#{baseurl}/#{encoded_path}"
172
167
  end
173
168
 
169
+ # Get the relative path from site source using Pathname
170
+ def relative_file_path(file_path, site)
171
+ file_pathname = Pathname.new(file_path)
172
+ if file_pathname.absolute? && file_path.to_s.start_with?(site.source)
173
+ file_pathname.relative_path_from(Pathname.new(site.source)).to_s
174
+ elsif file_path.start_with?("/")
175
+ file_path[1..]
176
+ else
177
+ file_path
178
+ end
179
+ end
180
+
174
181
  # Operation collection methods - these should NOT execute immediately
175
182
  def resize(width, height, options = {})
176
183
  @operations << { type: :resize, width: width, height: height,
@@ -21,19 +21,8 @@ module JekyllImgFlow
21
21
  # Calculate missing dimension to maintain aspect ratio ONLY when one dimension is missing
22
22
  width, height = calculate_missing_dimension(width, height, original_width, original_height)
23
23
 
24
- # Calculate scale factors for provider
25
- scale_x = width && original_width ? width.to_f / original_width : nil
26
- scale_y = height && original_height ? height.to_f / original_height : nil
27
-
28
24
  # Provider receives ALL data - provider chooses what to use
29
- resize_data = {
30
- original_width: original_width,
31
- original_height: original_height,
32
- target_width: width,
33
- target_height: height,
34
- scale_x: scale_x,
35
- scale_y: scale_y
36
- }
25
+ resize_data = build_resize_data(width, height, original_width, original_height)
37
26
 
38
27
  Jekyll.logger.debug "🔍 ResizeTag: About to execute provider - input: #{input_path}, output: #{output_path}"
39
28
  @provider.resize(width, height, resize_data)
@@ -46,6 +35,17 @@ module JekyllImgFlow
46
35
 
47
36
  private
48
37
 
38
+ def build_resize_data(width, height, original_width, original_height)
39
+ {
40
+ original_width: original_width,
41
+ original_height: original_height,
42
+ target_width: width,
43
+ target_height: height,
44
+ scale_x: width && original_width ? width.to_f / original_width : nil,
45
+ scale_y: height && original_height ? height.to_f / original_height : nil
46
+ }
47
+ end
48
+
49
49
  def calculate_missing_dimension(width, height, original_width, original_height)
50
50
  if width.nil? && height
51
51
  # Only height provided - calculate width to maintain aspect ratio
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JekyllImgFlow
4
- VERSION = "0.4.0"
4
+ VERSION = "0.4.2"
5
5
  end
@@ -29,6 +29,7 @@ require_relative "jekyll-imgflow/imgflow_tag"
29
29
  require_relative "jekyll-imgflow/picture_tag_adaptor"
30
30
  require_relative "jekyll-imgflow/picture_tag_preset_migrator"
31
31
  require_relative "jekyll-imgflow/hooks"
32
+ require_relative "jekyll-imgflow/interface"
32
33
 
33
34
  module JekyllImgFlow
34
35
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-imgflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Svend Gundestrup
@@ -62,6 +62,7 @@ extra_rdoc_files: []
62
62
  files:
63
63
  - LICENSE
64
64
  - README.md
65
+ - interface.yml
65
66
  - lib/jekyll-imgflow.rb
66
67
  - lib/jekyll-imgflow/animated_gif_detector.rb
67
68
  - lib/jekyll-imgflow/batch_manager.rb
@@ -73,6 +74,7 @@ files:
73
74
  - lib/jekyll-imgflow/hooks.rb
74
75
  - lib/jekyll-imgflow/html_generator.rb
75
76
  - lib/jekyll-imgflow/imgflow_tag.rb
77
+ - lib/jekyll-imgflow/interface.rb
76
78
  - lib/jekyll-imgflow/manifest_manager.rb
77
79
  - lib/jekyll-imgflow/modal_assets.rb
78
80
  - lib/jekyll-imgflow/operation_processor.rb