jekyll-documents 0.4.0 → 0.6.1
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/CHANGELOG.md +59 -0
- data/README.md +182 -1
- data/_includes/category_list.html +1 -1
- data/assets/css/documents.css +34 -0
- data/assets/js/documents-search.js +1 -1
- data/jekyll-documents.gemspec +7 -7
- data/lib/jekyll/documents/configuration.rb +5 -0
- data/lib/jekyll/documents/file_type_icons.rb +5 -3
- data/lib/jekyll/documents/generator.rb +131 -1
- data/lib/jekyll/documents/layout_registrar.rb +9 -0
- data/lib/jekyll/documents/tags/doc_link.rb +3 -1
- data/lib/jekyll/documents/tags/document_icon.rb +4 -3
- data/lib/jekyll/documents/text_extraction_manifest.rb +193 -0
- data/lib/jekyll/documents/version.rb +1 -1
- data/lib/jekyll-documents.rb +1 -0
- metadata +27 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: be2955da22172cc24f7aed4e3dca0ac968bf131040a36db5fe2ba048a64bca90
|
|
4
|
+
data.tar.gz: 2b3d0544fa7c86739e2d83cd9228ee24c64f6bc4ff7691211aefbc7f3533f837
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1f2c7c21a5cdd8c5e996379467b2ceb38d69e368aa9f8bdae882a657366911fb1262f66a7c8ff549d848b1304fd90a8e56e9f37efe1b856f572fab550899d3d5
|
|
7
|
+
data.tar.gz: fce8b514c14788e75405a026dcd6e7eabf25955a12ac16445380f995728cf6f2c65099ab42af18c51c8fb2c1361f3f3bb9cdfa1845a3642e4495e532640a7d16
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,65 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.6.1] - 2026-09-03
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- Text extraction cache keys now include the source digest, extraction configuration, Plaintext version, and cache schema version so configuration or extractor changes invalidate stale results
|
|
9
|
+
- Extracted document content now retains searchable metadata for title, category, file type, and date
|
|
10
|
+
- Text cache cleanup now preserves shared digest files and removes orphaned files safely
|
|
11
|
+
- Text extraction now enforces `text_max_bytes` as a UTF-8 byte limit and closes source files after reading
|
|
12
|
+
- PDF extraction now discovers `pdftotext` from `PATH`; added macOS and Debian/Ubuntu setup instructions and CI coverage with `poppler-utils`
|
|
13
|
+
- Configured text cache directories are excluded from Jekyll output regardless of the `extract_text` setting
|
|
14
|
+
- Rake packaging and test tasks no longer perform duplicate work and install only the current package artifact
|
|
15
|
+
- Removed redundant package metadata and duplicate development dependency declarations
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- Replaced use of the private ActiveSupport deprecation API with the public `ActiveSupport.deprecator` API
|
|
19
|
+
- Empty extraction results now fall back to metadata-only searchable content
|
|
20
|
+
- Manifest loading now validates entries, logs filesystem read failures, and resets dirty state after successful saves
|
|
21
|
+
- Updated stale CI, development, cache-cleanup, and package-lock documentation
|
|
22
|
+
|
|
23
|
+
### Tests
|
|
24
|
+
- Added real PDF extraction coverage and stronger cache-hit/persistence assertions
|
|
25
|
+
- Added tests for cache configuration invalidation, UTF-8 byte truncation, shared cache references, orphan cleanup, and conditional cache exclusion
|
|
26
|
+
|
|
27
|
+
## [0.6.0] - 2026-08-27
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
- Auto-inject `file_type`, `icon_url`, and `icon_set` into jekyll-client-search's `passthrough_fields` config when `documents` is in the search collections — zero extra config needed for icons in search results
|
|
31
|
+
- Documented field renaming in jekyll-client-search for integration with other search conventions
|
|
32
|
+
- Framework-agnostic CSS file (`assets/css/documents.css`) with icon scaling utility classes (`icon-x1` through `icon-x9`: 16px to 512px) — no dependency on Bulma, Bootstrap, or Tailwind
|
|
33
|
+
- Icons default to `1em` (line-height) so they scale with surrounding text
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
- Renamed icon CSS class from `file-icon` to `document-file-icon` to avoid collision with Bulma's `.file-icon` (which uses `display: flex` and breaks inline SVG icons onto a separate line)
|
|
37
|
+
- Inline `1em` sizing added to JS-rendered and folder icons as a fallback when the CSS file is not included
|
|
38
|
+
|
|
39
|
+
## [0.5.0] - 2026-08-27
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
- Text extraction from PDF, DOCX, XLSX, PPTX, ODT, ODS, and ODP files via the optional `plaintext` gem, enabling full-text search of document contents through `jekyll-client-search`
|
|
43
|
+
- `extract_text` configuration option to enable text extraction (disabled by default)
|
|
44
|
+
- `text_max_bytes` configuration option to control extracted text truncation (default 500KB)
|
|
45
|
+
- `text_cache_dir` configuration option for the persistent cache directory (default `.cache/jekyll-documents`)
|
|
46
|
+
- `TextExtractionManifest` class with SHA-256 content-based cache invalidation, atomic writes, sharded text file storage, and automatic cleanup of entries for deleted source files
|
|
47
|
+
- Cache directory excluded from Jekyll build output via a `:site, :after_init` hook
|
|
48
|
+
- 9 integration tests with real DOCX and ODT fixtures validating extraction, caching, manifest persistence, and cleanup
|
|
49
|
+
- `plaintext` as a development dependency for testing extraction
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
- Minimum Ruby version raised from 3.3 to 3.4 (tested on Ruby 3.4.10)
|
|
53
|
+
- RuboCop target version updated from 3.3 to 3.4
|
|
54
|
+
- Updated development dependencies: `rubocop` ~> 1.90, `rubocop-performance` ~> 1.27, `simplecov` ~> 1.1, `rake` ~> 13.4
|
|
55
|
+
- Updated Gemfile pins: `google-protobuf` ~> 4.36, `sass-embedded` ~> 1.103
|
|
56
|
+
- CI workflow matrix simplified to Ruby 3.4 only (dropped 3.3)
|
|
57
|
+
- `documentation_uri` in gemspec metadata now points to DeepWiki instead of duplicating the GitHub repo URL
|
|
58
|
+
- Removed redundant `homepage_uri` from gemspec metadata (already covered by `spec.homepage`)
|
|
59
|
+
- Suppressed ActiveSupport deprecation warnings from the `plaintext` gem (`String#mb_chars`, deprecated in Rails 8.2)
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
- `CHANGELOG.md` file permissions corrected to be world-readable (was `600`, now `644`)
|
|
63
|
+
|
|
5
64
|
## [0.4.0] - 2026-08-26
|
|
6
65
|
|
|
7
66
|
### Added
|
data/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
Turn files in `assets/documents/` into browsable document pages.
|
|
11
11
|
|
|
12
|
-
**Requirements**: Ruby 3.
|
|
12
|
+
**Requirements**: Ruby 3.4+ • Jekyll 4.4+
|
|
13
13
|
|
|
14
14
|
**Features**: Auto-collection • File icons • Categories • Search
|
|
15
15
|
|
|
@@ -120,6 +120,187 @@ documents:
|
|
|
120
120
|
|
|
121
121
|
See [configuration.rb](lib/jekyll/documents/configuration.rb) for all options.
|
|
122
122
|
|
|
123
|
+
## Icon sizing
|
|
124
|
+
|
|
125
|
+
Icons default to `1em` (line-height) so they scale with surrounding text.
|
|
126
|
+
A framework-agnostic CSS file with fixed-size utility classes is included:
|
|
127
|
+
|
|
128
|
+
```html
|
|
129
|
+
<link rel="stylesheet" href="{{ '/assets/css/documents.css' | relative_url }}">
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
| Class | Size |
|
|
133
|
+
| --- | --- |
|
|
134
|
+
| (default) | `1em` — scales with line height |
|
|
135
|
+
| `icon-x1` | 16px |
|
|
136
|
+
| `icon-x2` | 32px |
|
|
137
|
+
| `icon-x3` | 48px |
|
|
138
|
+
| `icon-x4` | 64px |
|
|
139
|
+
| `icon-x5` | 96px |
|
|
140
|
+
| `icon-x6` | 128px |
|
|
141
|
+
| `icon-x7` | 150px |
|
|
142
|
+
| `icon-x8` | 256px |
|
|
143
|
+
| `icon-x9` | 512px |
|
|
144
|
+
|
|
145
|
+
Usage with the `document_icon` tag:
|
|
146
|
+
|
|
147
|
+
```liquid
|
|
148
|
+
{% document_icon page class:"document-file-icon icon-x2" %}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Or with any `<img>`:
|
|
152
|
+
|
|
153
|
+
```html
|
|
154
|
+
<img src="..." class="document-file-icon icon-x3" />
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The CSS uses `display: inline-block` and `vertical-align: middle` — no
|
|
158
|
+
dependency on Bulma, Bootstrap, Tailwind, or any other framework.
|
|
159
|
+
|
|
160
|
+
## Search Integration
|
|
161
|
+
|
|
162
|
+
Documents are compatible with [jekyll-client-search](https://github.com/gundestrup/jekyll-client-search)
|
|
163
|
+
for client-side search. Each document has `categories` (plural array) and
|
|
164
|
+
searchable `content` baked in at generation time, so search plugins can
|
|
165
|
+
index uploaded files alongside posts.
|
|
166
|
+
|
|
167
|
+
To include documents in the search index, add `documents` to the
|
|
168
|
+
`collections` list in `_config.yml`:
|
|
169
|
+
|
|
170
|
+
```yaml
|
|
171
|
+
client_search:
|
|
172
|
+
collections:
|
|
173
|
+
- posts
|
|
174
|
+
- documents
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Icons and file-type metadata in search results
|
|
178
|
+
|
|
179
|
+
jekyll-documents bakes `file_type`, `icon_url`, and `icon_set` into each
|
|
180
|
+
document's data at generation time. When jekyll-client-search is installed
|
|
181
|
+
and `documents` is in the search collections, these fields are
|
|
182
|
+
**auto-injected** into the search index — no extra configuration needed:
|
|
183
|
+
|
|
184
|
+
```yaml
|
|
185
|
+
client_search:
|
|
186
|
+
collections:
|
|
187
|
+
- posts
|
|
188
|
+
- documents
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
This makes jekyll-client-search render:
|
|
192
|
+
|
|
193
|
+
- `data-file-type`, `data-icon-set` attributes on each result `<article>`
|
|
194
|
+
(for CSS-based badges and theme-aware styling)
|
|
195
|
+
- An `<img class="client-search-result-icon">` before the title, using the
|
|
196
|
+
icon from the configured `icon_set` (color, lines, minimal, or ultra-minimal)
|
|
197
|
+
|
|
198
|
+
The icon automatically matches the `icon_set` configured in your `documents`
|
|
199
|
+
section — no extra configuration needed for theme consistency.
|
|
200
|
+
|
|
201
|
+
**Field renaming** (for integration with other search conventions):
|
|
202
|
+
|
|
203
|
+
```yaml
|
|
204
|
+
client_search:
|
|
205
|
+
passthrough_fields:
|
|
206
|
+
- file_type: doctype # rename in the search index
|
|
207
|
+
- icon_url: thumbnail
|
|
208
|
+
icon_field: thumbnail
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**CSS examples:**
|
|
212
|
+
|
|
213
|
+
```css
|
|
214
|
+
/* File-type badge */
|
|
215
|
+
.client-search-result[data-file-type="pdf"]::before {
|
|
216
|
+
content: "PDF";
|
|
217
|
+
background: #e74c3c; color: white;
|
|
218
|
+
padding: 0 0.3em; font-size: 0.7em; margin-right: 0.3em;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* Theme-aware icon sizing */
|
|
222
|
+
.client-search-result[data-icon-set="color"] .client-search-result-icon { width: 2em; }
|
|
223
|
+
.client-search-result[data-icon-set="ultra-minimal"] .client-search-result-icon { width: 1em; }
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
See the [jekyll-client-search README](https://github.com/gundestrup/jekyll-client-search#customizing-search-results-with-css)
|
|
227
|
+
for all CSS customization options.
|
|
228
|
+
|
|
229
|
+
## Text Extraction
|
|
230
|
+
|
|
231
|
+
Extract text from PDF/DOCX/XLSX/PPTX/ODT/ODS/ODP files so search engines
|
|
232
|
+
can index document contents, not just metadata.
|
|
233
|
+
|
|
234
|
+
### Setup
|
|
235
|
+
|
|
236
|
+
Add the optional [`plaintext`](https://github.com/planio-gmbh/plaintext) gem
|
|
237
|
+
to your Gemfile:
|
|
238
|
+
|
|
239
|
+
```ruby
|
|
240
|
+
# Gemfile
|
|
241
|
+
gem "plaintext", group: :jekyll_plugins
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Enable extraction in `_config.yml`:
|
|
245
|
+
|
|
246
|
+
```yaml
|
|
247
|
+
documents:
|
|
248
|
+
extract_text: true
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### How it works
|
|
252
|
+
|
|
253
|
+
- Extracted text is stored in `doc.content`, which `jekyll-client-search`
|
|
254
|
+
indexes automatically — no extra configuration needed
|
|
255
|
+
- Text is cached in `.cache/jekyll-documents/` (in your site source,
|
|
256
|
+
not `.jekyll-cache/`), so it **survives `jekyll clean`**
|
|
257
|
+
- Cache invalidation uses **SHA-256 file digests** — only changed files
|
|
258
|
+
are re-extracted
|
|
259
|
+
- Stale cache entries for deleted files are cleaned up automatically
|
|
260
|
+
at the end of each build
|
|
261
|
+
- Falls back to metadata-only content (title, category, file type, date)
|
|
262
|
+
if the `plaintext` gem is missing or extraction fails
|
|
263
|
+
|
|
264
|
+
### Configuration
|
|
265
|
+
|
|
266
|
+
```yaml
|
|
267
|
+
documents:
|
|
268
|
+
extract_text: true # Enable text extraction
|
|
269
|
+
text_max_bytes: 500000 # Truncate extracted text (default 500KB)
|
|
270
|
+
text_cache_dir: ".cache/jekyll-documents" # Cache directory in site source
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Add the cache directory to `.gitignore`:
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
.cache/jekyll-documents/
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### CLI tool dependencies
|
|
280
|
+
|
|
281
|
+
The `plaintext` gem uses the `rubyzip` Ruby gem for Office formats (no CLI
|
|
282
|
+
tools needed). PDF extraction shells out to a system command:
|
|
283
|
+
|
|
284
|
+
| Format | Tool |
|
|
285
|
+
|--------|------|
|
|
286
|
+
| PDF | `pdftotext` (poppler-utils) |
|
|
287
|
+
| DOCX/PPTX/XLSX | rubyzip (Ruby gem, no CLI needed) |
|
|
288
|
+
| ODT/ODS/ODP | rubyzip (Ruby gem, no CLI needed) |
|
|
289
|
+
|
|
290
|
+
Install `pdftotext` for PDF extraction:
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
# macOS
|
|
294
|
+
brew install poppler
|
|
295
|
+
|
|
296
|
+
# Debian/Ubuntu
|
|
297
|
+
sudo apt-get install poppler-utils
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
On macOS and other non-standard installations, the executable is discovered
|
|
301
|
+
from `PATH`. You can also configure a custom `pdftotext` command through the
|
|
302
|
+
`plaintext` gem configuration.
|
|
303
|
+
|
|
123
304
|
## Development
|
|
124
305
|
|
|
125
306
|
```bash
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<ul class="documents-category-list">
|
|
5
5
|
{% for cat in cats %}
|
|
6
6
|
<li>
|
|
7
|
-
<img src="{{ '/assets/icons/' | append: icon_set | append: '/folder-svgrepo-com.svg' | relative_url }}" alt="Folder" class="folder-icon" />
|
|
7
|
+
<img src="{{ '/assets/icons/' | append: icon_set | append: '/folder-svgrepo-com.svg' | relative_url }}" alt="Folder" class="folder-icon" style="width:1em;height:1em;vertical-align:middle;" />
|
|
8
8
|
<span class="category-name">{{ cat }}</span>
|
|
9
9
|
<span class="category-count">({{ site.documents | where: "category", cat | size }})</span>
|
|
10
10
|
</li>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* jekyll-documents — icon sizing utilities
|
|
3
|
+
*
|
|
4
|
+
* Framework-agnostic: no dependency on Bulma, Bootstrap, Tailwind, etc.
|
|
5
|
+
* Icons default to 1em (line-height) so they scale with surrounding text.
|
|
6
|
+
* Override with .icon-x{N} classes for fixed pixel sizes.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* {% document_icon page class:"document-file-icon icon-x2" %}
|
|
10
|
+
* <img src="..." class="document-file-icon icon-x3" />
|
|
11
|
+
*
|
|
12
|
+
* Include this stylesheet in your site:
|
|
13
|
+
* <link rel="stylesheet" href="{{ '/assets/css/documents.css' | relative_url }}">
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/* Default: icon scales with line height */
|
|
17
|
+
.document-file-icon,
|
|
18
|
+
.folder-icon {
|
|
19
|
+
width: 1em;
|
|
20
|
+
height: 1em;
|
|
21
|
+
vertical-align: middle;
|
|
22
|
+
display: inline-block;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Fixed pixel sizes */
|
|
26
|
+
.icon-x1 { width: 16px; height: 16px; }
|
|
27
|
+
.icon-x2 { width: 32px; height: 32px; }
|
|
28
|
+
.icon-x3 { width: 48px; height: 48px; }
|
|
29
|
+
.icon-x4 { width: 64px; height: 64px; }
|
|
30
|
+
.icon-x5 { width: 96px; height: 96px; }
|
|
31
|
+
.icon-x6 { width: 128px; height: 128px; }
|
|
32
|
+
.icon-x7 { width: 150px; height: 150px; }
|
|
33
|
+
.icon-x8 { width: 256px; height: 256px; }
|
|
34
|
+
.icon-x9 { width: 512px; height: 512px; }
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
if (!iconUrl) return "";
|
|
33
33
|
const altText = `${String(fileType || "file").toUpperCase()} file`;
|
|
34
34
|
const url = escapeHtml(withBaseurl(iconUrl));
|
|
35
|
-
return `<img src="${url}" alt="${escapeHtml(altText)}" class="file-icon" />`;
|
|
35
|
+
return `<img src="${url}" alt="${escapeHtml(altText)}" class="document-file-icon" style="width:1em;height:1em;vertical-align:middle;" />`;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
function render(matches) {
|
data/jekyll-documents.gemspec
CHANGED
|
@@ -20,12 +20,11 @@ Gem::Specification.new do |spec|
|
|
|
20
20
|
"source_code_uri" => "https://github.com/gundestrup/jekyll-documents",
|
|
21
21
|
"bug_tracker_uri" => "https://github.com/gundestrup/jekyll-documents/issues",
|
|
22
22
|
"changelog_uri" => "https://github.com/gundestrup/jekyll-documents/blob/main/CHANGELOG.md",
|
|
23
|
-
"documentation_uri" => "https://
|
|
24
|
-
"homepage_uri" => "https://github.com/gundestrup/jekyll-documents",
|
|
23
|
+
"documentation_uri" => "https://deepwiki.com/gundestrup/jekyll-documents",
|
|
25
24
|
"rubygems_mfa_required" => "true"
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
spec.required_ruby_version = ">= 3.
|
|
27
|
+
spec.required_ruby_version = ">= 3.4"
|
|
29
28
|
|
|
30
29
|
spec.files = Dir.glob("{lib,assets,_includes,_layouts}/**/*") +
|
|
31
30
|
["README.md", "CHANGELOG.md", "LICENSE", "jekyll-documents.gemspec"]
|
|
@@ -34,10 +33,11 @@ Gem::Specification.new do |spec|
|
|
|
34
33
|
spec.add_dependency "jekyll", ">= 4.4", "< 5.0"
|
|
35
34
|
|
|
36
35
|
spec.add_development_dependency "bundler-audit", "~> 0.9"
|
|
37
|
-
spec.add_development_dependency "
|
|
36
|
+
spec.add_development_dependency "plaintext", "~> 0.3"
|
|
37
|
+
spec.add_development_dependency "rake", "~> 13.4"
|
|
38
38
|
spec.add_development_dependency "rspec", "~> 3.13"
|
|
39
|
-
spec.add_development_dependency "rubocop", "~> 1.
|
|
40
|
-
spec.add_development_dependency "rubocop-performance", "~> 1.
|
|
41
|
-
spec.add_development_dependency "simplecov", "~> 1.
|
|
39
|
+
spec.add_development_dependency "rubocop", "~> 1.90"
|
|
40
|
+
spec.add_development_dependency "rubocop-performance", "~> 1.27"
|
|
41
|
+
spec.add_development_dependency "simplecov", "~> 1.1"
|
|
42
42
|
spec.add_development_dependency "yard", "~> 0.9"
|
|
43
43
|
end
|
|
@@ -24,6 +24,11 @@ module Jekyll
|
|
|
24
24
|
"json_index" => true,
|
|
25
25
|
"json_index_path" => "/documents.json",
|
|
26
26
|
|
|
27
|
+
# Text extraction for search indexing (requires optional 'plaintext' gem)
|
|
28
|
+
"extract_text" => false,
|
|
29
|
+
"text_max_bytes" => 500_000,
|
|
30
|
+
"text_cache_dir" => ".cache/jekyll-documents",
|
|
31
|
+
|
|
27
32
|
# Optional category mapping
|
|
28
33
|
"category_map" => {}
|
|
29
34
|
}.freeze
|
|
@@ -154,11 +154,13 @@ module Jekyll
|
|
|
154
154
|
# @return [String] HTML img tag
|
|
155
155
|
# @example
|
|
156
156
|
# file_type_icon_tag('pdf') #=> \
|
|
157
|
-
# '<img src="
|
|
158
|
-
def file_type_icon_tag(file_type, css_class: "file-icon", alt: nil, context: nil)
|
|
157
|
+
# '<img src="..." alt="PDF file" class="document-file-icon" ... />'
|
|
158
|
+
def file_type_icon_tag(file_type, css_class: "document-file-icon", alt: nil, context: nil)
|
|
159
159
|
url = file_type_icon(file_type, context)
|
|
160
160
|
alt_text = alt || "#{file_type.to_s.upcase} file"
|
|
161
|
-
|
|
161
|
+
style = "width:1em;height:1em;vertical-align:middle;"
|
|
162
|
+
"<img src=\"#{url}\" alt=\"#{alt_text}\" " \
|
|
163
|
+
"class=\"#{css_class}\" style=\"#{style}\" />"
|
|
162
164
|
end
|
|
163
165
|
|
|
164
166
|
private
|
|
@@ -10,12 +10,25 @@ module Jekyll
|
|
|
10
10
|
safe true
|
|
11
11
|
priority :normal
|
|
12
12
|
|
|
13
|
+
CONTENT_TYPES = {
|
|
14
|
+
".pdf" => "application/pdf",
|
|
15
|
+
".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
16
|
+
".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
17
|
+
".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
18
|
+
".odt" => "application/vnd.oasis.opendocument.text",
|
|
19
|
+
".ods" => "application/vnd.oasis.opendocument.spreadsheet",
|
|
20
|
+
".odp" => "application/vnd.oasis.opendocument.presentation"
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
private_constant :CONTENT_TYPES
|
|
24
|
+
|
|
13
25
|
# Generates document collection from files in configured root directory
|
|
14
26
|
# @param site [Jekyll::Site] the Jekyll site instance
|
|
15
27
|
# @return [void]
|
|
16
28
|
def generate(site)
|
|
17
29
|
@site = site
|
|
18
30
|
@config = Configuration.read(site)
|
|
31
|
+
@text_manifest = nil
|
|
19
32
|
|
|
20
33
|
root = File.join(site.source, @config["root"])
|
|
21
34
|
unless Dir.exist?(root)
|
|
@@ -25,6 +38,8 @@ module Jekyll
|
|
|
25
38
|
|
|
26
39
|
collection = ensure_collection(site, "documents")
|
|
27
40
|
|
|
41
|
+
current_paths = []
|
|
42
|
+
|
|
28
43
|
Dir.glob("#{root}/**/*").each do |path|
|
|
29
44
|
next unless File.file?(path)
|
|
30
45
|
|
|
@@ -36,6 +51,7 @@ module Jekyll
|
|
|
36
51
|
next unless @config["include_extensions"].include?(ext)
|
|
37
52
|
|
|
38
53
|
rel_path = path.delete_prefix("#{site.source}/")
|
|
54
|
+
current_paths << rel_path
|
|
39
55
|
category = infer_category_from(rel_path)
|
|
40
56
|
basename = File.basename(path, ext)
|
|
41
57
|
|
|
@@ -61,10 +77,31 @@ module Jekyll
|
|
|
61
77
|
bake_document_data(doc, file_info)
|
|
62
78
|
collection.docs << doc
|
|
63
79
|
end
|
|
80
|
+
|
|
81
|
+
cleanup_manifest(current_paths) if @config["extract_text"]
|
|
82
|
+
configure_client_search(site)
|
|
64
83
|
end
|
|
65
84
|
|
|
66
85
|
private
|
|
67
86
|
|
|
87
|
+
# Auto-injects passthrough_fields into client_search config when the
|
|
88
|
+
# documents collection is indexed. Only acts if client_search is already
|
|
89
|
+
# configured with +documents+ in its collections list — does nothing if
|
|
90
|
+
# jekyll-client-search is not installed or not used.
|
|
91
|
+
def configure_client_search(site)
|
|
92
|
+
search_config = site.config["client_search"]
|
|
93
|
+
return unless search_config.is_a?(Hash)
|
|
94
|
+
return unless Array(search_config["collections"]).include?("documents")
|
|
95
|
+
|
|
96
|
+
fields = search_config["passthrough_fields"] || []
|
|
97
|
+
existing = fields.flat_map { |f| f.is_a?(Hash) ? f.keys : [f.to_s] }
|
|
98
|
+
%w[file_type icon_url icon_set].each do |field|
|
|
99
|
+
fields << field unless existing.include?(field)
|
|
100
|
+
end
|
|
101
|
+
search_config["passthrough_fields"] = fields
|
|
102
|
+
search_config["icon_field"] = "icon_url" unless search_config.key?("icon_field")
|
|
103
|
+
end
|
|
104
|
+
|
|
68
105
|
# Ensures a collection exists and is configured for output
|
|
69
106
|
# @param site [Jekyll::Site] the Jekyll site instance
|
|
70
107
|
# @param label [String] the collection name
|
|
@@ -83,6 +120,89 @@ module Jekyll
|
|
|
83
120
|
"#{title} #{data['category']} #{file_type} #{date_str}"
|
|
84
121
|
end
|
|
85
122
|
|
|
123
|
+
def extract_file_content(info)
|
|
124
|
+
return nil unless load_plaintext
|
|
125
|
+
|
|
126
|
+
content_type = CONTENT_TYPES[info[:ext]]
|
|
127
|
+
return nil unless content_type
|
|
128
|
+
|
|
129
|
+
manifest = text_manifest
|
|
130
|
+
rel_path = info[:rel_path]
|
|
131
|
+
digest = ::Digest::SHA256.file(info[:path]).hexdigest
|
|
132
|
+
metadata = extraction_cache_metadata
|
|
133
|
+
|
|
134
|
+
cached = manifest.get(rel_path, digest, metadata)
|
|
135
|
+
return cached if cached
|
|
136
|
+
|
|
137
|
+
configure_pdf_extractor(content_type)
|
|
138
|
+
text = ActiveSupport.deprecator.silence do
|
|
139
|
+
File.open(info[:path], "rb") do |file|
|
|
140
|
+
::Plaintext::Resolver.new(file, content_type).text
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
text = truncate_bytes(text, @config["text_max_bytes"]) if text
|
|
144
|
+
manifest.set(rel_path, digest, text, metadata) if text
|
|
145
|
+
text
|
|
146
|
+
rescue StandardError => e
|
|
147
|
+
::Jekyll.logger.warn "jekyll-documents",
|
|
148
|
+
"Text extraction failed for #{info[:path]}: #{e.message}"
|
|
149
|
+
nil
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def configure_pdf_extractor(content_type)
|
|
153
|
+
return unless content_type == "application/pdf"
|
|
154
|
+
return unless ::Plaintext::Configuration["pdftotext"].nil?
|
|
155
|
+
|
|
156
|
+
executable = ENV.fetch("PATH", "").split(File::PATH_SEPARATOR)
|
|
157
|
+
.map { |directory| File.join(directory, "pdftotext") }
|
|
158
|
+
.find { |path| File.executable?(path) }
|
|
159
|
+
return unless executable
|
|
160
|
+
|
|
161
|
+
::Plaintext::Configuration.config["pdftotext"] =
|
|
162
|
+
[executable, "-enc", "UTF-8", "__FILE__", "-"]
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def load_plaintext
|
|
166
|
+
return true if defined?(::Plaintext)
|
|
167
|
+
|
|
168
|
+
require "plaintext"
|
|
169
|
+
true
|
|
170
|
+
rescue LoadError
|
|
171
|
+
::Jekyll.logger.warn "jekyll-documents",
|
|
172
|
+
"extract_text is enabled but the 'plaintext' gem is not installed. " \
|
|
173
|
+
"Run: gem install plaintext"
|
|
174
|
+
false
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def text_manifest
|
|
178
|
+
@text_manifest ||= TextExtractionManifest.new(@site, @config["text_cache_dir"])
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def extraction_cache_metadata
|
|
182
|
+
{
|
|
183
|
+
"schema_version" => 1,
|
|
184
|
+
"text_max_bytes" => @config["text_max_bytes"],
|
|
185
|
+
"plaintext_version" => if ::Plaintext.const_defined?(:VERSION)
|
|
186
|
+
::Plaintext::VERSION.to_s
|
|
187
|
+
else
|
|
188
|
+
"unknown"
|
|
189
|
+
end
|
|
190
|
+
}
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def truncate_bytes(text, max_bytes)
|
|
194
|
+
bytes = text.to_s.encode("UTF-8", invalid: :replace, undef: :replace)
|
|
195
|
+
.byteslice(0, max_bytes)
|
|
196
|
+
bytes = bytes.to_s.force_encoding("UTF-8")
|
|
197
|
+
bytes = bytes.byteslice(0, bytes.bytesize - 1) until bytes.valid_encoding?
|
|
198
|
+
bytes
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def cleanup_manifest(current_rel_paths)
|
|
202
|
+
text_manifest.cleanup_deleted(current_rel_paths)
|
|
203
|
+
text_manifest.save
|
|
204
|
+
end
|
|
205
|
+
|
|
86
206
|
def bake_document_data(doc, info)
|
|
87
207
|
data = doc.data
|
|
88
208
|
category = remap_category(info[:category])
|
|
@@ -102,7 +222,17 @@ module Jekyll
|
|
|
102
222
|
data["permalink"] = @config["permalink"]
|
|
103
223
|
.gsub(":category", category.to_s)
|
|
104
224
|
.gsub(":slug", info[:slug])
|
|
105
|
-
|
|
225
|
+
metadata_content = searchable_content(info[:title], data, info[:file_type])
|
|
226
|
+
doc.content = if @config["extract_text"]
|
|
227
|
+
extracted_content = extract_file_content(info)
|
|
228
|
+
if extracted_content && !extracted_content.empty?
|
|
229
|
+
"#{metadata_content} #{extracted_content}"
|
|
230
|
+
else
|
|
231
|
+
metadata_content
|
|
232
|
+
end
|
|
233
|
+
else
|
|
234
|
+
metadata_content
|
|
235
|
+
end
|
|
106
236
|
end
|
|
107
237
|
|
|
108
238
|
# Creates a virtual source path for the document
|
|
@@ -57,4 +57,13 @@ end
|
|
|
57
57
|
|
|
58
58
|
Jekyll::Hooks.register :site, :after_init do |site|
|
|
59
59
|
Jekyll::Documents::LayoutRegistrar.register(site)
|
|
60
|
+
|
|
61
|
+
# Exclude the text extraction cache directory from Jekyll output
|
|
62
|
+
config = Jekyll::Documents::Configuration.read(site)
|
|
63
|
+
if config["text_cache_dir"]
|
|
64
|
+
site.config["exclude"] = Array(site.config["exclude"])
|
|
65
|
+
unless site.config["exclude"].include?(config["text_cache_dir"])
|
|
66
|
+
site.config["exclude"] << config["text_cache_dir"]
|
|
67
|
+
end
|
|
68
|
+
end
|
|
60
69
|
end
|
|
@@ -63,7 +63,9 @@ module Jekyll
|
|
|
63
63
|
|
|
64
64
|
url = relative_url(icon_url)
|
|
65
65
|
file_type = doc.data["file_type"].to_s.upcase
|
|
66
|
-
"<img src=\"#{escape_html(url)}\" alt=\"#{file_type}\"
|
|
66
|
+
"<img src=\"#{escape_html(url)}\" alt=\"#{file_type}\" " \
|
|
67
|
+
"class=\"document-file-icon doc-link-icon\" " \
|
|
68
|
+
"style=\"width:1em;height:1em;vertical-align:middle;\" />"
|
|
67
69
|
end
|
|
68
70
|
|
|
69
71
|
def size_html(doc)
|
|
@@ -21,10 +21,11 @@ module Jekyll
|
|
|
21
21
|
url = relative_url(icon_url)
|
|
22
22
|
file_type = document_value(document, "file_type").to_s
|
|
23
23
|
alt = @options["alt"] || "#{file_type.upcase} file"
|
|
24
|
-
css_class = @options["class"] || "file-icon"
|
|
24
|
+
css_class = @options["class"] || "document-file-icon"
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
style = "width:1em;height:1em;vertical-align:middle;"
|
|
27
|
+
"<img src=\"#{escape_html(url)}\" alt=\"#{escape_html(alt)}\" " \
|
|
28
|
+
"class=\"#{escape_html(css_class)}\" style=\"#{style}\" />"
|
|
28
29
|
end
|
|
29
30
|
|
|
30
31
|
private
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "digest"
|
|
6
|
+
|
|
7
|
+
module Jekyll
|
|
8
|
+
module Documents
|
|
9
|
+
module TextExtractionManifestSupport
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def load_manifest
|
|
13
|
+
return {} unless File.file?(@manifest_path)
|
|
14
|
+
|
|
15
|
+
data = JSON.parse(File.read(@manifest_path, encoding: "UTF-8"))
|
|
16
|
+
data.is_a?(Hash) ? data.select { |_path, entry| valid_entry?(entry) } : {}
|
|
17
|
+
rescue JSON::ParserError => e
|
|
18
|
+
::Jekyll.logger.warn "jekyll-documents",
|
|
19
|
+
"Corrupt text extraction manifest, starting fresh: #{e.message}"
|
|
20
|
+
{}
|
|
21
|
+
rescue StandardError => e
|
|
22
|
+
::Jekyll.logger.warn "jekyll-documents",
|
|
23
|
+
"Text extraction manifest read failed: #{e.message}"
|
|
24
|
+
{}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def valid_entry?(entry)
|
|
28
|
+
entry.is_a?(Hash) && entry.values_at("digest", "cache_key", "text_file").all?(String)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def cache_key(digest, metadata)
|
|
32
|
+
Digest::SHA256.hexdigest(JSON.generate("digest" => digest, "metadata" => metadata))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def text_file_path(text_file)
|
|
36
|
+
return unless text_file.is_a?(String)
|
|
37
|
+
|
|
38
|
+
path = File.expand_path(File.join(@text_dir, text_file))
|
|
39
|
+
root = File.expand_path(@text_dir)
|
|
40
|
+
path.start_with?("#{root}#{File::SEPARATOR}") ? path : nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def write_text_file(digest, text)
|
|
44
|
+
shard = digest[0, 2]
|
|
45
|
+
subdir = File.join(@text_dir, shard)
|
|
46
|
+
FileUtils.mkdir_p(subdir)
|
|
47
|
+
filename = "#{digest}.txt"
|
|
48
|
+
File.write(File.join(subdir, filename), text, encoding: "UTF-8")
|
|
49
|
+
File.join(shard, filename)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def cleanup_unreferenced_text_files
|
|
53
|
+
referenced = @manifest.values.filter_map do |entry|
|
|
54
|
+
text_file_path(entry["text_file"]) if valid_entry?(entry)
|
|
55
|
+
end.to_set
|
|
56
|
+
Dir.glob(File.join(@text_dir, "**", "*.txt")).each do |path|
|
|
57
|
+
File.delete(path) unless referenced.include?(File.expand_path(path))
|
|
58
|
+
end
|
|
59
|
+
rescue StandardError
|
|
60
|
+
nil
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Manages the text extraction manifest — a persistent JSON file that tracks
|
|
65
|
+
# which documents have had their text extracted, keyed by SHA-256 digest.
|
|
66
|
+
#
|
|
67
|
+
# The manifest lives in <site.source>/<cache_dir>/text-extraction-manifest.json
|
|
68
|
+
# and extracted text is stored in separate files under <cache_dir>/text/
|
|
69
|
+
# (sharded by the first 2 hex chars of the digest to avoid huge directories).
|
|
70
|
+
#
|
|
71
|
+
# This design borrows from jekyll-imgflow's ManifestManager:
|
|
72
|
+
# - Atomic writes (temp file + rename)
|
|
73
|
+
# - SHA-256 content digests for cache invalidation
|
|
74
|
+
# - Cleanup of entries for deleted source files
|
|
75
|
+
# - Survives `jekyll clean` (stored in site source, not .jekyll-cache/)
|
|
76
|
+
class TextExtractionManifest
|
|
77
|
+
include TextExtractionManifestSupport
|
|
78
|
+
|
|
79
|
+
MANIFEST_FILENAME = "text-extraction-manifest.json"
|
|
80
|
+
TEXT_SUBDIR = "text"
|
|
81
|
+
|
|
82
|
+
attr_reader :manifest_path, :cache_dir
|
|
83
|
+
|
|
84
|
+
# @param site [Jekyll::Site] the Jekyll site instance
|
|
85
|
+
# @param cache_dir [String] relative path from site source for cache directory
|
|
86
|
+
def initialize(site, cache_dir)
|
|
87
|
+
@site = site
|
|
88
|
+
@cache_dir = cache_dir
|
|
89
|
+
@cache_root = File.join(site.source, cache_dir)
|
|
90
|
+
@manifest_path = File.join(@cache_root, MANIFEST_FILENAME)
|
|
91
|
+
@text_dir = File.join(@cache_root, TEXT_SUBDIR)
|
|
92
|
+
@manifest = load_manifest
|
|
93
|
+
@dirty = false
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Get cached text for a document if the digest matches.
|
|
97
|
+
# @param rel_path [String] relative path of the source document
|
|
98
|
+
# @param digest [String] SHA-256 hex digest of the source file
|
|
99
|
+
# @return [String, nil] extracted text if cache hit, nil otherwise
|
|
100
|
+
def get(rel_path, digest, metadata = {})
|
|
101
|
+
entry = @manifest[rel_path]
|
|
102
|
+
return nil unless valid_entry?(entry)
|
|
103
|
+
return nil unless entry["cache_key"] == cache_key(digest, metadata)
|
|
104
|
+
|
|
105
|
+
text_file = text_file_path(entry["text_file"])
|
|
106
|
+
return nil unless text_file && File.file?(text_file)
|
|
107
|
+
|
|
108
|
+
File.read(text_file, encoding: "UTF-8")
|
|
109
|
+
rescue StandardError => e
|
|
110
|
+
::Jekyll.logger.warn "jekyll-documents",
|
|
111
|
+
"Manifest read failed for #{rel_path}: #{e.message}"
|
|
112
|
+
nil
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Store extracted text for a document.
|
|
116
|
+
# @param rel_path [String] relative path of the source document
|
|
117
|
+
# @param digest [String] SHA-256 hex digest of the source file
|
|
118
|
+
# @param text [String] the extracted text
|
|
119
|
+
# @return [void]
|
|
120
|
+
def set(rel_path, digest, text, metadata = {})
|
|
121
|
+
text_file = write_text_file(digest, text)
|
|
122
|
+
@manifest[rel_path] = {
|
|
123
|
+
"digest" => digest,
|
|
124
|
+
"cache_key" => cache_key(digest, metadata),
|
|
125
|
+
"text_file" => text_file,
|
|
126
|
+
"extracted_at" => Time.now.to_i
|
|
127
|
+
}
|
|
128
|
+
cleanup_unreferenced_text_files
|
|
129
|
+
@dirty = true
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Remove manifest entries and their text files for source documents
|
|
133
|
+
# that no longer exist.
|
|
134
|
+
# @param current_rel_paths [Array<String>] relative paths of current source files
|
|
135
|
+
# @return [Integer] number of entries removed
|
|
136
|
+
def cleanup_deleted(current_rel_paths)
|
|
137
|
+
current_set = current_rel_paths.to_set
|
|
138
|
+
removed = 0
|
|
139
|
+
|
|
140
|
+
@manifest.each_key do |rel_path|
|
|
141
|
+
next if current_set.include?(rel_path)
|
|
142
|
+
|
|
143
|
+
@manifest.delete(rel_path)
|
|
144
|
+
removed += 1
|
|
145
|
+
@dirty = true
|
|
146
|
+
end
|
|
147
|
+
cleanup_unreferenced_text_files
|
|
148
|
+
|
|
149
|
+
removed
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Save the manifest to disk if it has changed.
|
|
153
|
+
# Uses atomic write (temp file + rename) to prevent corruption.
|
|
154
|
+
# @return [void]
|
|
155
|
+
def save
|
|
156
|
+
return unless @dirty
|
|
157
|
+
|
|
158
|
+
content = JSON.pretty_generate(@manifest)
|
|
159
|
+
if File.exist?(@manifest_path) && File.binread(@manifest_path) == content
|
|
160
|
+
@dirty = false
|
|
161
|
+
return
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
FileUtils.mkdir_p(@cache_root)
|
|
165
|
+
temporary_path = "#{@manifest_path}.tmp-#{Process.pid}-#{Thread.current.object_id}"
|
|
166
|
+
File.open(temporary_path, "wb") do |file|
|
|
167
|
+
file.write(content)
|
|
168
|
+
file.flush
|
|
169
|
+
file.fsync
|
|
170
|
+
end
|
|
171
|
+
File.rename(temporary_path, @manifest_path)
|
|
172
|
+
@dirty = false
|
|
173
|
+
ensure
|
|
174
|
+
FileUtils.rm_f(temporary_path) if defined?(temporary_path) && temporary_path
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Check if a document is in the manifest with a matching digest.
|
|
178
|
+
# @param rel_path [String] relative path of the source document
|
|
179
|
+
# @param digest [String] SHA-256 hex digest of the source file
|
|
180
|
+
# @return [Boolean]
|
|
181
|
+
def cached?(rel_path, digest, metadata = {})
|
|
182
|
+
entry = @manifest[rel_path]
|
|
183
|
+
!!(valid_entry?(entry) && entry["cache_key"] == cache_key(digest, metadata))
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Number of entries in the manifest.
|
|
187
|
+
# @return [Integer]
|
|
188
|
+
def size
|
|
189
|
+
@manifest.size
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
data/lib/jekyll-documents.rb
CHANGED
|
@@ -7,6 +7,7 @@ require_relative "jekyll/documents/utils"
|
|
|
7
7
|
require_relative "jekyll/documents/filters"
|
|
8
8
|
require_relative "jekyll/documents/file_type_icons"
|
|
9
9
|
require_relative "jekyll/documents/generator"
|
|
10
|
+
require_relative "jekyll/documents/text_extraction_manifest"
|
|
10
11
|
require_relative "jekyll/documents/assets_generator"
|
|
11
12
|
require_relative "jekyll/documents/json_index_generator"
|
|
12
13
|
require_relative "jekyll/documents/layout_registrar"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jekyll-documents
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Svend Gundestrup
|
|
@@ -43,20 +43,34 @@ dependencies:
|
|
|
43
43
|
- - "~>"
|
|
44
44
|
- !ruby/object:Gem::Version
|
|
45
45
|
version: '0.9'
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: plaintext
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - "~>"
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '0.3'
|
|
53
|
+
type: :development
|
|
54
|
+
prerelease: false
|
|
55
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - "~>"
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '0.3'
|
|
46
60
|
- !ruby/object:Gem::Dependency
|
|
47
61
|
name: rake
|
|
48
62
|
requirement: !ruby/object:Gem::Requirement
|
|
49
63
|
requirements:
|
|
50
64
|
- - "~>"
|
|
51
65
|
- !ruby/object:Gem::Version
|
|
52
|
-
version: '13.
|
|
66
|
+
version: '13.4'
|
|
53
67
|
type: :development
|
|
54
68
|
prerelease: false
|
|
55
69
|
version_requirements: !ruby/object:Gem::Requirement
|
|
56
70
|
requirements:
|
|
57
71
|
- - "~>"
|
|
58
72
|
- !ruby/object:Gem::Version
|
|
59
|
-
version: '13.
|
|
73
|
+
version: '13.4'
|
|
60
74
|
- !ruby/object:Gem::Dependency
|
|
61
75
|
name: rspec
|
|
62
76
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -77,42 +91,42 @@ dependencies:
|
|
|
77
91
|
requirements:
|
|
78
92
|
- - "~>"
|
|
79
93
|
- !ruby/object:Gem::Version
|
|
80
|
-
version: '1.
|
|
94
|
+
version: '1.90'
|
|
81
95
|
type: :development
|
|
82
96
|
prerelease: false
|
|
83
97
|
version_requirements: !ruby/object:Gem::Requirement
|
|
84
98
|
requirements:
|
|
85
99
|
- - "~>"
|
|
86
100
|
- !ruby/object:Gem::Version
|
|
87
|
-
version: '1.
|
|
101
|
+
version: '1.90'
|
|
88
102
|
- !ruby/object:Gem::Dependency
|
|
89
103
|
name: rubocop-performance
|
|
90
104
|
requirement: !ruby/object:Gem::Requirement
|
|
91
105
|
requirements:
|
|
92
106
|
- - "~>"
|
|
93
107
|
- !ruby/object:Gem::Version
|
|
94
|
-
version: '1.
|
|
108
|
+
version: '1.27'
|
|
95
109
|
type: :development
|
|
96
110
|
prerelease: false
|
|
97
111
|
version_requirements: !ruby/object:Gem::Requirement
|
|
98
112
|
requirements:
|
|
99
113
|
- - "~>"
|
|
100
114
|
- !ruby/object:Gem::Version
|
|
101
|
-
version: '1.
|
|
115
|
+
version: '1.27'
|
|
102
116
|
- !ruby/object:Gem::Dependency
|
|
103
117
|
name: simplecov
|
|
104
118
|
requirement: !ruby/object:Gem::Requirement
|
|
105
119
|
requirements:
|
|
106
120
|
- - "~>"
|
|
107
121
|
- !ruby/object:Gem::Version
|
|
108
|
-
version: '1.
|
|
122
|
+
version: '1.1'
|
|
109
123
|
type: :development
|
|
110
124
|
prerelease: false
|
|
111
125
|
version_requirements: !ruby/object:Gem::Requirement
|
|
112
126
|
requirements:
|
|
113
127
|
- - "~>"
|
|
114
128
|
- !ruby/object:Gem::Version
|
|
115
|
-
version: '1.
|
|
129
|
+
version: '1.1'
|
|
116
130
|
- !ruby/object:Gem::Dependency
|
|
117
131
|
name: yard
|
|
118
132
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -144,6 +158,7 @@ files:
|
|
|
144
158
|
- _includes/documents_search.html
|
|
145
159
|
- _includes/latest_documents.html
|
|
146
160
|
- _layouts/document.html
|
|
161
|
+
- assets/css/documents.css
|
|
147
162
|
- assets/icons/color/ai-document-svgrepo-com.svg
|
|
148
163
|
- assets/icons/color/attachment-document-svgrepo-com.svg
|
|
149
164
|
- assets/icons/color/audio-document-svgrepo-com.svg
|
|
@@ -255,6 +270,7 @@ files:
|
|
|
255
270
|
- lib/jekyll/documents/tags/doc_link.rb
|
|
256
271
|
- lib/jekyll/documents/tags/document_icon.rb
|
|
257
272
|
- lib/jekyll/documents/tags/latest_documents.rb
|
|
273
|
+
- lib/jekyll/documents/text_extraction_manifest.rb
|
|
258
274
|
- lib/jekyll/documents/utils.rb
|
|
259
275
|
- lib/jekyll/documents/version.rb
|
|
260
276
|
homepage: https://github.com/gundestrup/jekyll-documents
|
|
@@ -264,8 +280,7 @@ metadata:
|
|
|
264
280
|
source_code_uri: https://github.com/gundestrup/jekyll-documents
|
|
265
281
|
bug_tracker_uri: https://github.com/gundestrup/jekyll-documents/issues
|
|
266
282
|
changelog_uri: https://github.com/gundestrup/jekyll-documents/blob/main/CHANGELOG.md
|
|
267
|
-
documentation_uri: https://
|
|
268
|
-
homepage_uri: https://github.com/gundestrup/jekyll-documents
|
|
283
|
+
documentation_uri: https://deepwiki.com/gundestrup/jekyll-documents
|
|
269
284
|
rubygems_mfa_required: 'true'
|
|
270
285
|
rdoc_options: []
|
|
271
286
|
require_paths:
|
|
@@ -274,7 +289,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
274
289
|
requirements:
|
|
275
290
|
- - ">="
|
|
276
291
|
- !ruby/object:Gem::Version
|
|
277
|
-
version: '3.
|
|
292
|
+
version: '3.4'
|
|
278
293
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
279
294
|
requirements:
|
|
280
295
|
- - ">="
|