jekyll-image-links 0.1.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +195 -0
- data/assets/jekyll-image-links/image_links.css +180 -0
- data/assets/jekyll-image-links/image_links.js +468 -0
- data/lib/jekyll/image_links/asset_file.rb +17 -0
- data/lib/jekyll/image_links/generator.rb +34 -0
- data/lib/jekyll/image_links/hooks.rb +61 -0
- data/lib/jekyll/image_links/map_renderer.rb +395 -0
- data/lib/jekyll/image_links/tags.rb +47 -0
- data/lib/jekyll/image_links/version.rb +5 -0
- data/lib/jekyll-image-links.rb +8 -0
- metadata +70 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 6b9c59cd6053f748b0f8dcf7552df298abb667f7298ff717af6acd4dffab5148
|
|
4
|
+
data.tar.gz: c3855e305456f92098aacd45ff07a8ae188b6eae6df1cc95cd9866faef3c96cf
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c8d2a5dc878d212dd91f299e837c09dc789bda8eefdbd745287a7bbd0c646df81694e8b9cdf05487fa68b231509661e3947a0d188defc955c1d3fa94ad77871d
|
|
7
|
+
data.tar.gz: '008678f930adeaad4ebcbdeaa93b32c87f16dc1fa99ab546634c4d89b3b21fc9b7f103f0e2a0c134d1f68f7c548cb0369c0232ea08645ff7de99fed48c41d6a3'
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 directsun
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# jekyll-image-links
|
|
2
|
+
|
|
3
|
+
A Jekyll plugin for [Just the Docs](https://github.com/just-the-docs/just-the-docs) sites that adds clickable polygon regions on images.
|
|
4
|
+
|
|
5
|
+
Inspired by the Dynamic Map Viewer in [5etools](https://github.com/5etools/5etools-src), which uses custom JavaScript (not HTML `<map>` tags) to define polygon click areas on large map images.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Define polygon click regions in Markdown using a Liquid tag
|
|
10
|
+
- Click a region to navigate to an internal or external link
|
|
11
|
+
- Hold **Shift** while clicking to open the link in a new tab
|
|
12
|
+
- Integrates with [jekyll-hover-popup](https://github.com/directsun/jekyll-hover-popup) when both plugins are enabled: region clicks open pinned hover-popup windows (no page darkening), and the map viewer opens inside a hover-popup window too
|
|
13
|
+
- Optional **Dynamic Map Viewer** button with zoom, pan, and region highlighting
|
|
14
|
+
- Optional region labels overlaid on the image
|
|
15
|
+
- Load region data inline or from a YAML file
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
Add to your site `Gemfile`:
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
group :jekyll_plugins do
|
|
23
|
+
gem "jekyll-image-links", path: "/path/to/image-links"
|
|
24
|
+
end
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Then in `_config.yml`:
|
|
28
|
+
|
|
29
|
+
```yml
|
|
30
|
+
plugins:
|
|
31
|
+
- jekyll-image-links
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Configuration
|
|
35
|
+
|
|
36
|
+
Optional `_config.yml` settings:
|
|
37
|
+
|
|
38
|
+
```yml
|
|
39
|
+
image_links:
|
|
40
|
+
enabled: true
|
|
41
|
+
assets_path: /assets/jekyll-image-links
|
|
42
|
+
viewer_by_default: true
|
|
43
|
+
inline_by_default: true
|
|
44
|
+
labels_by_default: false
|
|
45
|
+
use_hover_popup: auto # auto | true | false
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
When `jekyll-hover-popup` is also installed, image map region clicks open section previews in hover-popup windows instead of navigating the page. Set `use_hover_popup: false` to disable that integration.
|
|
49
|
+
|
|
50
|
+
## Usage
|
|
51
|
+
|
|
52
|
+
### Inline regions
|
|
53
|
+
|
|
54
|
+
```liquid
|
|
55
|
+
{% image_map src="/assets/maps/example.webp" width="1200" height="800" title="Example Map" %}
|
|
56
|
+
- href: /docs/room-a/
|
|
57
|
+
title: Room A
|
|
58
|
+
label: A
|
|
59
|
+
points:
|
|
60
|
+
- [120, 80]
|
|
61
|
+
- [420, 80]
|
|
62
|
+
- [420, 320]
|
|
63
|
+
- [120, 320]
|
|
64
|
+
- href: /docs/room-b/
|
|
65
|
+
title: Room B
|
|
66
|
+
label: B
|
|
67
|
+
points:
|
|
68
|
+
- [480, 120]
|
|
69
|
+
- [760, 120]
|
|
70
|
+
- [760, 420]
|
|
71
|
+
- [480, 420]
|
|
72
|
+
{% endimage_map %}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### External YAML file
|
|
76
|
+
|
|
77
|
+
`assets/maps/example-map.yml`:
|
|
78
|
+
|
|
79
|
+
```yml
|
|
80
|
+
width: 1200
|
|
81
|
+
height: 800
|
|
82
|
+
regions:
|
|
83
|
+
- href: /docs/room-a/
|
|
84
|
+
title: Room A
|
|
85
|
+
points: [[120, 80], [420, 80], [420, 320], [120, 320]]
|
|
86
|
+
- href: /docs/room-b/
|
|
87
|
+
title: Room B
|
|
88
|
+
points: [[480, 120], [760, 120], [760, 420], [480, 420]]
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
When `width` and `height` are in the YAML file, you can omit them from the tag and control display size separately:
|
|
92
|
+
|
|
93
|
+
```liquid
|
|
94
|
+
{% image_map src="/assets/maps/example.webp" file="assets/maps/example-map.yml" style="width:100%;max-width:900px;height:auto" %}
|
|
95
|
+
{% endimage_map %}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Legacy form still works — numeric `width`/`height` on the tag or `<img>` define the native coordinate system when the YAML file does not include them:
|
|
99
|
+
|
|
100
|
+
```liquid
|
|
101
|
+
{% image_map src="/assets/maps/example.webp" width="1200" height="800" file="assets/maps/example-map.yml" %}
|
|
102
|
+
{% endimage_map %}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Tag options
|
|
106
|
+
|
|
107
|
+
| Attribute | Description |
|
|
108
|
+
|-----------|-------------|
|
|
109
|
+
| `src` | Image URL (required) |
|
|
110
|
+
| `width` | Native image width in pixels (required unless set in YAML) |
|
|
111
|
+
| `height` | Native image height in pixels (required unless set in YAML) |
|
|
112
|
+
| `title` | Caption and viewer title |
|
|
113
|
+
| `alt` | Image alt text (defaults to `title`) |
|
|
114
|
+
| `file` | Path to a YAML regions file relative to site source |
|
|
115
|
+
| `style` | CSS display sizing (for example `width:100%;max-width:900px;height:auto`) |
|
|
116
|
+
| `viewer="false"` | Disable the Dynamic Map Viewer button |
|
|
117
|
+
| `inline="false"` | Disable direct click regions on the inline image |
|
|
118
|
+
| `labels="true"` | Show region labels on the inline image |
|
|
119
|
+
|
|
120
|
+
### Manual HTML (portable markup)
|
|
121
|
+
|
|
122
|
+
You can also write image maps directly in Markdown using `{::nomarkdown}` blocks. Only an `<img>` tag is required — no `<figure>` wrapper. Put configuration on the image itself using `data-jil-*` attributes.
|
|
123
|
+
|
|
124
|
+
Store the **native** image dimensions in the YAML file. Region `points` use that coordinate system. Use CSS on the `<img>` for **display** size (percentages, max width/height, and so on). At build time those display styles are moved to the surrounding `.jil-map-host` wrapper so percentage sizes resolve against the page layout correctly.
|
|
125
|
+
|
|
126
|
+
If the plugin is disabled or not installed, the image still renders normally and browsers ignore the extra `data-jil-*` attributes.
|
|
127
|
+
|
|
128
|
+
```markdown
|
|
129
|
+
{::nomarkdown}
|
|
130
|
+
<img
|
|
131
|
+
class="jil-map-image"
|
|
132
|
+
src="/assets/images/a-familiar-tower/tower-1-combined-cropped.png"
|
|
133
|
+
alt="Level 1"
|
|
134
|
+
style="width: 100%; max-width: 900px; height: auto;"
|
|
135
|
+
loading="lazy"
|
|
136
|
+
data-jil-title="Level 1"
|
|
137
|
+
data-jil-regions="assets/maps/a-familiar-tower-1.yml"
|
|
138
|
+
data-jil-viewer="true"
|
|
139
|
+
data-jil-inline="true"
|
|
140
|
+
data-jil-labels="true"
|
|
141
|
+
/>
|
|
142
|
+
{:/nomarkdown}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`assets/maps/a-familiar-tower-1.yml`:
|
|
146
|
+
|
|
147
|
+
```yml
|
|
148
|
+
width: 1413
|
|
149
|
+
height: 1455
|
|
150
|
+
regions:
|
|
151
|
+
- href: /docs/room-a/
|
|
152
|
+
title: Room A
|
|
153
|
+
points: [[120, 80], [420, 80], [420, 320], [120, 320]]
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Display sizing alternatives on the `<img>`:
|
|
157
|
+
|
|
158
|
+
| Attribute | Example | Description |
|
|
159
|
+
|-----------|---------|-------------|
|
|
160
|
+
| `style` | `width:100%;max-width:900px;height:auto` | Full CSS control (recommended) |
|
|
161
|
+
| `width` / `height` | `width="100%"` or `width="800"` | Display size when YAML provides native dimensions |
|
|
162
|
+
| `data-jil-max-width` | `900px` or `80%` | Shorthand for `max-width` |
|
|
163
|
+
| `data-jil-max-height` | `600px` | Shorthand for `max-height` |
|
|
164
|
+
|
|
165
|
+
Portable images are detected by `class="jil-map-image"` together with region data (`data-jil-regions`, an adjacent `<script class="jil-regions-data">` block, or legacy `data-jil-map="true"`). Legacy `<figure data-jil-map="true">` markup is still supported.
|
|
166
|
+
|
|
167
|
+
| Attribute | Description |
|
|
168
|
+
|-----------|-------------|
|
|
169
|
+
| `class="jil-map-image"` | Marks the image for enhancement (required) |
|
|
170
|
+
| `data-jil-regions` | Path to a YAML regions file relative to site source |
|
|
171
|
+
| `data-jil-title` | Caption and viewer title |
|
|
172
|
+
| `data-jil-viewer` | Enable/disable the Dynamic Map Viewer button |
|
|
173
|
+
| `data-jil-inline` | Enable/disable direct click regions on the inline image |
|
|
174
|
+
| `data-jil-labels` | Show region labels on the inline image |
|
|
175
|
+
|
|
176
|
+
## Coordinate system
|
|
177
|
+
|
|
178
|
+
Region `points` use the image's native pixel coordinates, matching the 5etools `mapRegions` format. Define the native size once in the YAML file (`width` and `height`) or on the tag/image when not using YAML dimensions. For example, a 4937×3439 map uses coordinates in that range.
|
|
179
|
+
|
|
180
|
+
The plugin scales clicks from whatever size the image is displayed at back into that native coordinate system. Changing display size with CSS, percentages, or max width/height does not require editing region points.
|
|
181
|
+
|
|
182
|
+
## How this relates to 5etools
|
|
183
|
+
|
|
184
|
+
5etools stores regions as `mapRegions` on image entries in JSON data, then renders them with a custom `RenderMap` class (`js/render-map.js`) that:
|
|
185
|
+
|
|
186
|
+
- draws the image on a `<canvas>`
|
|
187
|
+
- overlays clickable polygons
|
|
188
|
+
- uses a ray-casting algorithm for hit detection
|
|
189
|
+
- opens linked adventure content in hover windows
|
|
190
|
+
|
|
191
|
+
This plugin uses the same polygon coordinate model and the same hit-detection approach, adapted for Jekyll pages with normal `href` links instead of 5etools' internal book area IDs.
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
MIT
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
.jil-figure {
|
|
2
|
+
margin: 1.25rem 0;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.jil-map-host {
|
|
6
|
+
position: relative;
|
|
7
|
+
display: block;
|
|
8
|
+
max-width: 100%;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.jil-map-host.jil-inline .jil-map-image {
|
|
12
|
+
display: block;
|
|
13
|
+
width: 100%;
|
|
14
|
+
height: auto;
|
|
15
|
+
max-width: 100%;
|
|
16
|
+
vertical-align: top;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.jil-map-host.jil-height-constrained {
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.jil-map-host.jil-height-constrained.jil-inline .jil-map-image {
|
|
24
|
+
width: auto;
|
|
25
|
+
max-width: 100%;
|
|
26
|
+
max-height: 100%;
|
|
27
|
+
height: auto;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.jil-caption {
|
|
31
|
+
margin-top: 0.5rem;
|
|
32
|
+
font-size: 0.95rem;
|
|
33
|
+
color: var(--jil-caption-color, inherit);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.jil-toolbar {
|
|
37
|
+
margin-top: 0.75rem;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.jil-viewer-button {
|
|
41
|
+
font: inherit;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.jil-label-layer {
|
|
45
|
+
position: absolute;
|
|
46
|
+
pointer-events: none;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.jil-label {
|
|
50
|
+
position: absolute;
|
|
51
|
+
transform: translate(-50%, -50%);
|
|
52
|
+
pointer-events: auto;
|
|
53
|
+
padding: 0.1rem 0.35rem;
|
|
54
|
+
border-radius: 0.2rem;
|
|
55
|
+
background: rgba(255, 255, 255, 0.82);
|
|
56
|
+
color: #1f2937;
|
|
57
|
+
font-size: 0.75rem;
|
|
58
|
+
font-weight: 700;
|
|
59
|
+
text-decoration: none;
|
|
60
|
+
line-height: 1.2;
|
|
61
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.jil-viewer-backdrop {
|
|
65
|
+
position: fixed;
|
|
66
|
+
inset: 0;
|
|
67
|
+
z-index: 10050;
|
|
68
|
+
display: flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
justify-content: center;
|
|
71
|
+
padding: 1rem;
|
|
72
|
+
background: rgba(0, 0, 0, 0.55);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.jil-viewer-panel {
|
|
76
|
+
display: flex;
|
|
77
|
+
flex-direction: column;
|
|
78
|
+
width: min(96vw, 1200px);
|
|
79
|
+
height: min(92vh, 900px);
|
|
80
|
+
background: var(--body-background-color, #fff);
|
|
81
|
+
color: var(--body-text-color, #272727);
|
|
82
|
+
border: 1px solid var(--border-color, #dee2e6);
|
|
83
|
+
border-radius: 0.35rem;
|
|
84
|
+
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
|
|
85
|
+
overflow: hidden;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.jil-viewer-backdrop .jil-viewer-panel {
|
|
89
|
+
width: min(96vw, 1200px);
|
|
90
|
+
height: min(92vh, 900px);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.jil-viewer-header {
|
|
94
|
+
display: flex;
|
|
95
|
+
align-items: center;
|
|
96
|
+
justify-content: space-between;
|
|
97
|
+
gap: 1rem;
|
|
98
|
+
padding: 0.75rem 1rem;
|
|
99
|
+
border-bottom: 1px solid var(--border-color, #dee2e6);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.jil-viewer-title {
|
|
103
|
+
font-weight: 700;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.jil-viewer-controls {
|
|
107
|
+
display: flex;
|
|
108
|
+
flex-wrap: wrap;
|
|
109
|
+
gap: 0.5rem;
|
|
110
|
+
padding: 0.75rem 1rem;
|
|
111
|
+
border-bottom: 1px solid var(--border-color, #dee2e6);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.jil-viewer-scroll {
|
|
115
|
+
flex: 1;
|
|
116
|
+
overflow: auto;
|
|
117
|
+
background: #111;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.jil-viewer-canvas {
|
|
121
|
+
display: block;
|
|
122
|
+
cursor: grab;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.jil-viewer-close {
|
|
126
|
+
margin-left: auto;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.jhp-hwin__content:has(.jil-viewer-in-popup) {
|
|
130
|
+
display: flex;
|
|
131
|
+
flex-direction: column;
|
|
132
|
+
overflow: hidden;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.jhp-hwin__content .jil-viewer-in-popup {
|
|
136
|
+
display: flex;
|
|
137
|
+
flex-direction: column;
|
|
138
|
+
flex: 1 1 auto;
|
|
139
|
+
width: 100%;
|
|
140
|
+
min-width: min(88vw, 760px);
|
|
141
|
+
min-height: 0;
|
|
142
|
+
max-height: none;
|
|
143
|
+
overflow: hidden;
|
|
144
|
+
background: transparent;
|
|
145
|
+
color: inherit;
|
|
146
|
+
border: 0;
|
|
147
|
+
border-radius: 0;
|
|
148
|
+
box-shadow: none;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.jhp-hwin__content .jil-viewer-in-popup .jil-viewer-header,
|
|
152
|
+
.jhp-hwin__content .jil-viewer-in-popup .jil-viewer-close {
|
|
153
|
+
display: none;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.jhp-hwin__content .jil-viewer-in-popup .jil-viewer-controls {
|
|
157
|
+
flex: 0 0 auto;
|
|
158
|
+
padding: 0.5rem 0 0.75rem;
|
|
159
|
+
border-bottom: 1px solid var(--jhp-border-color, var(--border-color, #dee2e6));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.jhp-hwin__content .jil-viewer-in-popup .jil-viewer-scroll {
|
|
163
|
+
flex: 1 1 auto;
|
|
164
|
+
min-height: 240px;
|
|
165
|
+
max-height: none;
|
|
166
|
+
overflow: auto;
|
|
167
|
+
background: #111;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.jhp-hwin__content .jil-viewer-in-popup .jil-viewer-canvas {
|
|
171
|
+
display: block;
|
|
172
|
+
cursor: grab;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
@media (prefers-color-scheme: dark) {
|
|
176
|
+
.jil-label {
|
|
177
|
+
background: rgba(24, 24, 24, 0.88);
|
|
178
|
+
color: #f3f4f6;
|
|
179
|
+
}
|
|
180
|
+
}
|