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 +4 -4
- data/README.md +20 -4
- data/interface.yml +63 -0
- data/lib/jekyll-imgflow/build_time_processor.rb +42 -29
- data/lib/jekyll-imgflow/config.rb +15 -8
- data/lib/jekyll-imgflow/hooks.rb +1 -1
- data/lib/jekyll-imgflow/html_generator.rb +5 -0
- data/lib/jekyll-imgflow/imgflow_tag.rb +2 -1
- data/lib/jekyll-imgflow/interface.rb +36 -0
- data/lib/jekyll-imgflow/modal_assets.rb +12 -6
- data/lib/jekyll-imgflow/parser.rb +2 -0
- data/lib/jekyll-imgflow/preset_manager.rb +23 -17
- data/lib/jekyll-imgflow/providers/base_provider.rb +26 -19
- data/lib/jekyll-imgflow/tags/resize_tag.rb +12 -12
- data/lib/jekyll-imgflow/version.rb +1 -1
- data/lib/jekyll-imgflow.rb +1 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c30d610c031609c2e1e9040c115d7162013db83ee6149d76bdb87bc97ad7137c
|
|
4
|
+
data.tar.gz: 8c9f212eb074af380df14d2f168f9d9379db196c8ec9b1a4111a850f804015e6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
[](https://github.com/gundestrup/jekyll-imgflow/actions/workflows/ci.yml)
|
|
8
|
+
[](https://rubygems.org/gems/jekyll-imgflow)
|
|
9
|
+
[](https://deepwiki.com/gundestrup/jekyll-imgflow)
|
|
8
10
|
[](https://github.com/gundestrup/jekyll-imgflow)
|
|
9
11
|
[](https://semgrep.dev/)
|
|
10
12
|
[](https://www.codefactor.io/repository/github/gundestrup/jekyll-imgflow)
|
|
13
|
+
[](https://sonarcloud.io/summary/new_code?id=gundestrup_jekyll-imgflow)
|
|
11
14
|
[](https://codecov.io/gh/gundestrup/jekyll-imgflow)
|
|
12
15
|
[](https://github.com/gundestrup/jekyll-imgflow-vscode)
|
|
13
16
|
[](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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
|
data/lib/jekyll-imgflow/hooks.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
when "
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
data/lib/jekyll-imgflow.rb
CHANGED
|
@@ -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.
|
|
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
|