jekyll-theme-zer0 1.3.0 → 1.4.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 +41 -0
- data/README.md +4 -4
- data/_includes/components/cookie-consent.html +2 -3
- data/_includes/components/info-section.html +9 -4
- data/_includes/components/js-cdn.html +8 -1
- data/_includes/content/backlinks.html +4 -0
- data/_includes/core/footer.html +9 -8
- data/_includes/navigation/local-graph.html +55 -20
- data/_includes/navigation/sidebar-left.html +0 -6
- data/_includes/obsidian/full-graph.html +170 -0
- data/_layouts/default.html +104 -100
- data/_plugins/obsidian_links.rb +5 -0
- data/_plugins/preview_image_generator.rb +82 -25
- data/_sass/core/_obsidian.scss +77 -4
- data/_sass/core/_offcanvas-panels.scss +8 -4
- data/assets/data/wiki-index.json +44 -6
- data/assets/js/obsidian-graph.js +0 -1
- data/assets/js/obsidian-local-graph.js +72 -21
- data/assets/js/obsidian-wiki-links.js +21 -4
- data/scripts/README.md +22 -0
- data/scripts/bin/test +62 -0
- data/scripts/bin/validate +596 -0
- data/scripts/lib/README.md +16 -0
- data/scripts/validate +11 -0
- metadata +5 -2
data/_layouts/default.html
CHANGED
|
@@ -1,100 +1,104 @@
|
|
|
1
|
-
---
|
|
2
|
-
layout: root
|
|
3
|
-
---
|
|
4
|
-
<!--
|
|
5
|
-
===================================================================
|
|
6
|
-
DEFAULT LAYOUT - Standard content layout with sidebars
|
|
7
|
-
===================================================================
|
|
8
|
-
|
|
9
|
-
File: default.html
|
|
10
|
-
Path: _layouts/default.html
|
|
11
|
-
Inherits: root.html
|
|
12
|
-
Purpose: Primary content layout with sidebar navigation and table of contents
|
|
13
|
-
|
|
14
|
-
Template Logic:
|
|
15
|
-
- Creates a responsive three-column layout using Bootstrap grid
|
|
16
|
-
- Left sidebar: Site navigation and content outline (collapsible on mobile)
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
- bd-
|
|
39
|
-
- bd-
|
|
40
|
-
- bd-
|
|
41
|
-
- bd-
|
|
42
|
-
- bd-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
<!--
|
|
48
|
-
<!--
|
|
49
|
-
<!--
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<!--
|
|
54
|
-
<!--
|
|
55
|
-
<!--
|
|
56
|
-
<!--
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
<!--
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
<!--
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
<!--
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
{%
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
{%-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
1
|
+
---
|
|
2
|
+
layout: root
|
|
3
|
+
---
|
|
4
|
+
<!--
|
|
5
|
+
===================================================================
|
|
6
|
+
DEFAULT LAYOUT - Standard content layout with sidebars
|
|
7
|
+
===================================================================
|
|
8
|
+
|
|
9
|
+
File: default.html
|
|
10
|
+
Path: _layouts/default.html
|
|
11
|
+
Inherits: root.html
|
|
12
|
+
Purpose: Primary content layout with sidebar navigation and table of contents
|
|
13
|
+
|
|
14
|
+
Template Logic:
|
|
15
|
+
- Creates a responsive three-column layout using Bootstrap grid
|
|
16
|
+
- Left sidebar: Site navigation and content outline (collapsible on mobile)
|
|
17
|
+
- Local graph: Independent Obsidian side panel with its own collapsible control
|
|
18
|
+
- Center: Main content area with page title and body content
|
|
19
|
+
- Right sidebar: Table of contents and page shortcuts (hidden on mobile)
|
|
20
|
+
- Implements scroll spy for navigation highlighting
|
|
21
|
+
- Responsive design that stacks vertically on mobile devices
|
|
22
|
+
|
|
23
|
+
Layout Structure:
|
|
24
|
+
1. Container wrapper with Bootstrap responsive classes
|
|
25
|
+
2. Left sidebar (bd-sidebar) - Navigation and outline
|
|
26
|
+
3. Main content area (bd-main) with:
|
|
27
|
+
- Intro section (page title, metadata)
|
|
28
|
+
- Right sidebar (bd-toc) - Table of contents
|
|
29
|
+
- Content area (bd-content) - Main page content
|
|
30
|
+
|
|
31
|
+
Dependencies:
|
|
32
|
+
- sidebar-left.html: Site navigation and content outline
|
|
33
|
+
- intro.html: Page title, breadcrumbs, and metadata
|
|
34
|
+
- sidebar-right.html: Table of contents and page shortcuts
|
|
35
|
+
|
|
36
|
+
Bootstrap Classes Used:
|
|
37
|
+
- container-xxl: Extra large responsive container
|
|
38
|
+
- bd-gutter: Custom Bootstrap spacing
|
|
39
|
+
- bd-layout: Custom layout utility class
|
|
40
|
+
- bd-sidebar: Custom sidebar styling
|
|
41
|
+
- bd-main: Main content area
|
|
42
|
+
- bd-toc: Table of contents styling
|
|
43
|
+
- bd-content: Content area styling
|
|
44
|
+
===================================================================
|
|
45
|
+
-->
|
|
46
|
+
|
|
47
|
+
<!-- ================================================ -->
|
|
48
|
+
<!-- MAIN LAYOUT CONTAINER -->
|
|
49
|
+
<!-- Bootstrap responsive container with custom grid -->
|
|
50
|
+
<!-- ================================================ -->
|
|
51
|
+
<div class="container-xxl bd-gutter mt-3 my-md-4 bd-layout">
|
|
52
|
+
|
|
53
|
+
<!-- ================================ -->
|
|
54
|
+
<!-- LEFT SIDEBAR - Navigation -->
|
|
55
|
+
<!-- ================================ -->
|
|
56
|
+
<!-- Site navigation, content outline, and offcanvas menu for mobile -->
|
|
57
|
+
<!-- Sidebar visibility controlled by page.sidebar front matter -->
|
|
58
|
+
{% unless page.sidebar == false %}
|
|
59
|
+
<aside class="bd-sidebar">
|
|
60
|
+
{% include navigation/sidebar-left.html %}
|
|
61
|
+
</aside>
|
|
62
|
+
|
|
63
|
+
<!-- Separate Obsidian local graph panel; intentionally outside bdSidebar. -->
|
|
64
|
+
{% include navigation/local-graph.html %}
|
|
65
|
+
{% endunless %}
|
|
66
|
+
|
|
67
|
+
<!-- ================================ -->
|
|
68
|
+
<!-- MAIN CONTENT AREA -->
|
|
69
|
+
<!-- ================================ -->
|
|
70
|
+
<!-- Primary content section with scroll spy for table of contents navigation -->
|
|
71
|
+
<main class="bd-main order-1" data-bs-spy="scroll" data-bs-target="#TableOfContents" data-bs-offset="100" data-bs-smooth-scroll="true">
|
|
72
|
+
|
|
73
|
+
<!-- Page introduction: title, breadcrumbs, metadata -->
|
|
74
|
+
{% include content/intro.html %}
|
|
75
|
+
|
|
76
|
+
<!-- =============================== -->
|
|
77
|
+
<!-- RIGHT SIDEBAR - Table of Contents -->
|
|
78
|
+
<!-- =============================== -->
|
|
79
|
+
<!-- Page outline, shortcuts, and related links (hidden on mobile) -->
|
|
80
|
+
<!-- Right sidebar visibility also controlled by page.sidebar -->
|
|
81
|
+
{% unless page.sidebar == false %}
|
|
82
|
+
<div class="bd-toc text-body-secondary">
|
|
83
|
+
{% include navigation/sidebar-right.html %}
|
|
84
|
+
</div>
|
|
85
|
+
{% endunless %}
|
|
86
|
+
|
|
87
|
+
<!-- =============================== -->
|
|
88
|
+
<!-- MAIN CONTENT BODY -->
|
|
89
|
+
<!-- =============================== -->
|
|
90
|
+
<!-- Where the actual page content is rendered -->
|
|
91
|
+
<div class="bd-content ps-lg-2">
|
|
92
|
+
{{ content }}
|
|
93
|
+
|
|
94
|
+
{%- comment -%}
|
|
95
|
+
Optional Obsidian-style backlinks panel. Opt-in for the generic
|
|
96
|
+
default layout via `backlinks: true` in front matter (the `note`
|
|
97
|
+
layout enables it by default).
|
|
98
|
+
{%- endcomment -%}
|
|
99
|
+
{% if page.backlinks == true %}
|
|
100
|
+
{% include content/backlinks.html %}
|
|
101
|
+
{% endif %}
|
|
102
|
+
</div>
|
|
103
|
+
</main>
|
|
104
|
+
</div>
|
data/_plugins/obsidian_links.rb
CHANGED
|
@@ -416,10 +416,15 @@ module Jekyll
|
|
|
416
416
|
|
|
417
417
|
def rewrite_document(doc)
|
|
418
418
|
return unless index
|
|
419
|
+
return unless needs_rewrite?(doc.content)
|
|
419
420
|
|
|
420
421
|
converter = Converter.new(nil, index, config)
|
|
421
422
|
doc.content = converter.convert(doc.content, current_url: doc.url)
|
|
422
423
|
end
|
|
424
|
+
|
|
425
|
+
def needs_rewrite?(content)
|
|
426
|
+
content.include?('[[') || content.include?('> [!') || content.match?(Converter::INLINE_TAG_RE)
|
|
427
|
+
end
|
|
423
428
|
end
|
|
424
429
|
end
|
|
425
430
|
end
|
|
@@ -47,6 +47,9 @@ module Jekyll
|
|
|
47
47
|
|
|
48
48
|
# Check if a document has a preview image defined
|
|
49
49
|
def self.has_preview?(doc)
|
|
50
|
+
cached = cached_preview_entry(doc)
|
|
51
|
+
return cached['has_preview'] unless cached.nil?
|
|
52
|
+
|
|
50
53
|
preview = doc.data['preview']
|
|
51
54
|
return false if preview.nil? || preview.to_s.strip.empty?
|
|
52
55
|
|
|
@@ -95,6 +98,9 @@ module Jekyll
|
|
|
95
98
|
|
|
96
99
|
# Get the preview image path for a document
|
|
97
100
|
def self.preview_path(doc)
|
|
101
|
+
cached = cached_preview_entry(doc)
|
|
102
|
+
return cached['path'] unless cached.nil?
|
|
103
|
+
|
|
98
104
|
preview = doc.data['preview']
|
|
99
105
|
return nil if preview.nil? || preview.to_s.strip.empty?
|
|
100
106
|
|
|
@@ -113,36 +119,86 @@ module Jekyll
|
|
|
113
119
|
|
|
114
120
|
# Get list of documents missing preview images
|
|
115
121
|
def self.missing_previews(site)
|
|
122
|
+
build_index(site)['missing']
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def self.build_index(site)
|
|
126
|
+
cached = site.data['preview_image_index']
|
|
127
|
+
return cached if cached
|
|
128
|
+
|
|
116
129
|
config = self.config(site)
|
|
130
|
+
index = {}
|
|
117
131
|
missing = []
|
|
118
|
-
|
|
119
|
-
config
|
|
132
|
+
|
|
133
|
+
preview_documents(site, config).each do |doc, collection_name|
|
|
134
|
+
key = preview_cache_key(doc)
|
|
135
|
+
next if index.key?(key)
|
|
136
|
+
|
|
137
|
+
preview = doc.data['preview']
|
|
138
|
+
normalized_path = nil
|
|
139
|
+
has_preview = false
|
|
140
|
+
|
|
141
|
+
if preview && !preview.to_s.strip.empty? && (preview.match?(/\.(png|jpe?g|gif|svg|webp)$/i) || preview.start_with?('http'))
|
|
142
|
+
normalized_path = preview.start_with?('http') ? preview : normalize_preview_path(preview, config)
|
|
143
|
+
has_preview = preview.start_with?('http') || preview_file_exists?(site, normalized_path, config)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
index[key] = {
|
|
147
|
+
'has_preview' => has_preview,
|
|
148
|
+
'path' => normalized_path,
|
|
149
|
+
'collection' => collection_name
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
next if has_preview
|
|
153
|
+
|
|
154
|
+
missing << {
|
|
155
|
+
'path' => doc.relative_path,
|
|
156
|
+
'title' => doc.data['title'] || File.basename(doc.relative_path),
|
|
157
|
+
'collection' => collection_name
|
|
158
|
+
}
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
site.data['preview_image_index'] = {
|
|
162
|
+
'documents' => index,
|
|
163
|
+
'missing' => missing
|
|
164
|
+
}
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def self.cached_preview_entry(doc)
|
|
168
|
+
return nil unless doc.respond_to?(:site) && doc.site && doc.site.data['preview_image_index']
|
|
169
|
+
|
|
170
|
+
doc.site.data['preview_image_index']['documents'][preview_cache_key(doc)]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def self.preview_documents(site, config)
|
|
174
|
+
docs = []
|
|
175
|
+
Array(config['collections']).each do |collection_name|
|
|
120
176
|
collection = site.collections[collection_name]
|
|
121
177
|
next unless collection
|
|
122
|
-
|
|
123
|
-
collection.docs.each
|
|
124
|
-
unless has_preview?(doc)
|
|
125
|
-
missing << {
|
|
126
|
-
'path' => doc.relative_path,
|
|
127
|
-
'title' => doc.data['title'] || File.basename(doc.relative_path),
|
|
128
|
-
'collection' => collection_name
|
|
129
|
-
}
|
|
130
|
-
end
|
|
131
|
-
end
|
|
178
|
+
|
|
179
|
+
collection.docs.each { |doc| docs << [doc, collection_name] }
|
|
132
180
|
end
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
181
|
+
|
|
182
|
+
site.posts.docs.each { |doc| docs << [doc, 'posts'] } if site.respond_to?(:posts) && site.posts
|
|
183
|
+
docs
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def self.preview_cache_key(doc)
|
|
187
|
+
doc.respond_to?(:relative_path) ? doc.relative_path : doc.path
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def self.preview_file_exists?(site, normalized_preview, config)
|
|
191
|
+
candidates = []
|
|
192
|
+
if normalized_preview.start_with?('/')
|
|
193
|
+
candidates << File.join(site.source, normalized_preview.sub(/^\//, ''))
|
|
194
|
+
candidates << File.join(site.source, 'assets', normalized_preview.sub(/^\//, ''))
|
|
195
|
+
else
|
|
196
|
+
candidates << File.join(site.source, 'assets', normalized_preview)
|
|
197
|
+
candidates << File.join(site.source, normalized_preview)
|
|
198
|
+
candidates << File.join(site.source, config['output_dir'], normalized_preview)
|
|
143
199
|
end
|
|
144
|
-
|
|
145
|
-
|
|
200
|
+
|
|
201
|
+
candidates.any? { |path| File.exist?(path) }
|
|
146
202
|
end
|
|
147
203
|
|
|
148
204
|
# Generate a preview image filename from document
|
|
@@ -264,7 +320,8 @@ module Jekyll
|
|
|
264
320
|
return unless config['enabled']
|
|
265
321
|
|
|
266
322
|
# Store missing previews in site data for access in templates
|
|
267
|
-
|
|
323
|
+
preview_index = PreviewImageGenerator.build_index(site)
|
|
324
|
+
site.data['preview_images_missing'] = preview_index['missing']
|
|
268
325
|
site.data['preview_images_config'] = config
|
|
269
326
|
|
|
270
327
|
# Log status
|
data/_sass/core/_obsidian.scss
CHANGED
|
@@ -152,18 +152,91 @@
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
// ----------------------------------------------------------------------------
|
|
155
|
-
// Local graph
|
|
155
|
+
// Local graph side panel — Obsidian-style local graph view.
|
|
156
156
|
// Rendered by _includes/navigation/local-graph.html via assets/js/obsidian-local-graph.js.
|
|
157
157
|
// ----------------------------------------------------------------------------
|
|
158
|
+
.obsidian-local-graph-fab {
|
|
159
|
+
position: fixed;
|
|
160
|
+
left: 1rem;
|
|
161
|
+
bottom: 1rem;
|
|
162
|
+
z-index: 1032;
|
|
163
|
+
|
|
164
|
+
&[hidden] {
|
|
165
|
+
display: none !important;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.obsidian-local-graph-toggle {
|
|
170
|
+
width: 3.25rem;
|
|
171
|
+
height: 3.25rem;
|
|
172
|
+
display: inline-flex;
|
|
173
|
+
align-items: center;
|
|
174
|
+
justify-content: center;
|
|
175
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
176
|
+
|
|
177
|
+
&:hover,
|
|
178
|
+
&:focus-visible {
|
|
179
|
+
transform: translateY(-1px);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
&:active {
|
|
183
|
+
transform: translateY(0);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.obsidian-local-graph-panel {
|
|
188
|
+
--bs-offcanvas-width: min(28rem, calc(100vw - 1.5rem));
|
|
189
|
+
|
|
190
|
+
&[hidden] {
|
|
191
|
+
display: none !important;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.offcanvas-body {
|
|
195
|
+
display: flex;
|
|
196
|
+
flex-direction: column;
|
|
197
|
+
min-height: 0;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
158
201
|
.obsidian-local-graph-widget {
|
|
202
|
+
display: flex;
|
|
203
|
+
flex: 1 1 auto;
|
|
204
|
+
flex-direction: column;
|
|
205
|
+
min-height: 0;
|
|
206
|
+
|
|
207
|
+
.obsidian-local-graph-meta {
|
|
208
|
+
flex: 0 0 auto;
|
|
209
|
+
}
|
|
210
|
+
|
|
159
211
|
#obsidian-local-graph {
|
|
212
|
+
flex: 1 1 auto;
|
|
160
213
|
width: 100%;
|
|
161
|
-
height:
|
|
162
|
-
min-height:
|
|
214
|
+
height: min(62vh, 34rem);
|
|
215
|
+
min-height: 22rem;
|
|
163
216
|
border: 1px solid var(--bs-border-color, #dee2e6);
|
|
164
|
-
border-radius: var(--bs-border-radius, 0.
|
|
217
|
+
border-radius: var(--bs-border-radius-lg, 0.5rem);
|
|
165
218
|
background: var(--bs-tertiary-bg, #f8f9fa);
|
|
166
219
|
overflow: hidden;
|
|
167
220
|
position: relative;
|
|
168
221
|
}
|
|
222
|
+
|
|
223
|
+
.obsidian-local-graph-status {
|
|
224
|
+
min-height: 1.25rem;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
@media (max-width: 575.98px) {
|
|
229
|
+
.obsidian-local-graph-fab {
|
|
230
|
+
left: 0.875rem;
|
|
231
|
+
bottom: 0.875rem;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.obsidian-local-graph-panel {
|
|
235
|
+
--bs-offcanvas-width: min(100vw, 24rem);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.obsidian-local-graph-widget #obsidian-local-graph {
|
|
239
|
+
height: 58vh;
|
|
240
|
+
min-height: 18rem;
|
|
241
|
+
}
|
|
169
242
|
}
|
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
// =============================================================================
|
|
5
5
|
|
|
6
6
|
#bdSidebar .offcanvas-header,
|
|
7
|
-
#tocContents .offcanvas-header
|
|
7
|
+
#tocContents .offcanvas-header,
|
|
8
|
+
#obsidianLocalGraphPanel .offcanvas-header {
|
|
8
9
|
border-bottom: 2px solid var(--bs-border-color);
|
|
9
10
|
padding: 1.25rem 1rem;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
#bdSidebar .offcanvas-title,
|
|
13
|
-
#tocContents .offcanvas-title
|
|
14
|
+
#tocContents .offcanvas-title,
|
|
15
|
+
#obsidianLocalGraphPanel .offcanvas-title {
|
|
14
16
|
font-size: 1.125rem;
|
|
15
17
|
font-weight: 600;
|
|
16
18
|
color: var(--bs-emphasis-color);
|
|
@@ -19,7 +21,8 @@
|
|
|
19
21
|
|
|
20
22
|
@media (max-width: 991.98px) {
|
|
21
23
|
#bdSidebar .offcanvas-header .btn-close,
|
|
22
|
-
#tocContents .offcanvas-header .btn-close
|
|
24
|
+
#tocContents .offcanvas-header .btn-close,
|
|
25
|
+
#obsidianLocalGraphPanel .offcanvas-header .btn-close {
|
|
23
26
|
width: 48px;
|
|
24
27
|
height: 48px;
|
|
25
28
|
padding: 0;
|
|
@@ -36,7 +39,8 @@
|
|
|
36
39
|
}
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
#tocContents .offcanvas-body
|
|
42
|
+
#tocContents .offcanvas-body,
|
|
43
|
+
#obsidianLocalGraphPanel .offcanvas-body {
|
|
40
44
|
padding: 1rem;
|
|
41
45
|
}
|
|
42
46
|
|
data/assets/data/wiki-index.json
CHANGED
|
@@ -36,14 +36,38 @@ sitemap: false
|
|
|
36
36
|
"entries": [
|
|
37
37
|
{%- for doc in all_docs -%}
|
|
38
38
|
{%- assign basename = doc.path | split: "/" | last | replace: ".md", "" | replace: ".markdown", "" | replace: ".html", "" -%}
|
|
39
|
-
{%- assign
|
|
39
|
+
{%- assign body_source = doc.content | default: "" -%}
|
|
40
|
+
{%- assign body_without_fences = "" -%}
|
|
41
|
+
{%- assign fence_parts = body_source | split: "```" -%}
|
|
42
|
+
{%- for fence_part in fence_parts -%}
|
|
43
|
+
{%- assign fence_mod = forloop.index0 | modulo: 2 -%}
|
|
44
|
+
{%- if fence_mod == 0 -%}
|
|
45
|
+
{%- assign body_without_fences = body_without_fences | append: fence_part -%}
|
|
46
|
+
{%- endif -%}
|
|
47
|
+
{%- endfor -%}
|
|
48
|
+
{%- assign body_without_code = "" -%}
|
|
49
|
+
{%- assign inline_code_parts = body_without_fences | split: "`" -%}
|
|
50
|
+
{%- for inline_code_part in inline_code_parts -%}
|
|
51
|
+
{%- assign inline_code_mod = forloop.index0 | modulo: 2 -%}
|
|
52
|
+
{%- if inline_code_mod == 0 -%}
|
|
53
|
+
{%- assign body_without_code = body_without_code | append: inline_code_part -%}
|
|
54
|
+
{%- endif -%}
|
|
55
|
+
{%- endfor -%}
|
|
56
|
+
{%- assign body = body_without_code | strip_html -%}
|
|
40
57
|
{%- assign body_excerpt = body | strip_newlines | truncate: 240 -%}
|
|
58
|
+
{%- assign aliases = "" | split: "," -%}
|
|
59
|
+
{%- if doc.aliases -%}
|
|
60
|
+
{%- assign aliases_blob = doc.aliases | join: "||" -%}
|
|
61
|
+
{%- assign aliases = aliases_blob | split: "||" -%}
|
|
62
|
+
{%- endif -%}
|
|
41
63
|
{%- comment -%}
|
|
42
64
|
Extract outgoing wiki-link targets so the graph view
|
|
43
65
|
(assets/js/obsidian-graph.js) can render edges without re-parsing
|
|
44
|
-
every page client-side.
|
|
45
|
-
|
|
46
|
-
|
|
66
|
+
every page client-side. Fenced and inline code are removed first so
|
|
67
|
+
Bash `[[ ... ]]` tests and literal examples don't become graph edges.
|
|
68
|
+
We mask `![[…]]` embeds so they don't also get split as `[[…]]`, then
|
|
69
|
+
strip optional `|alias` and `#anchor` / `^block` parts. Operator-heavy
|
|
70
|
+
targets are discarded as table examples rather than Obsidian page names.
|
|
47
71
|
{%- endcomment -%}
|
|
48
72
|
{%- assign masked = body | replace: "![[", "@@OBSEMBED@@" -%}
|
|
49
73
|
{%- assign chunks = masked | split: "[[" -%}
|
|
@@ -52,7 +76,21 @@ sitemap: false
|
|
|
52
76
|
{%- assign target_raw = chunk | split: "]]" | first -%}
|
|
53
77
|
{%- if target_raw and target_raw != "" -%}
|
|
54
78
|
{%- assign target = target_raw | split: "|" | first | split: "#" | first | split: "^" | first | strip | downcase -%}
|
|
55
|
-
{%-
|
|
79
|
+
{%- assign target_is_code = false -%}
|
|
80
|
+
{%- assign first_char = target | slice: 0 -%}
|
|
81
|
+
{%- if target contains "$" or target contains "==" or target contains "!=" or target contains "=~" -%}
|
|
82
|
+
{%- assign target_is_code = true -%}
|
|
83
|
+
{%- endif -%}
|
|
84
|
+
{%- if target contains "&&" or target contains "||" or target contains "&&" or target contains "<" or target contains ">" -%}
|
|
85
|
+
{%- assign target_is_code = true -%}
|
|
86
|
+
{%- endif -%}
|
|
87
|
+
{%- if target contains " -eq " or target contains " -ne " or target contains " -lt " or target contains " -le " or target contains " -gt " or target contains " -ge " -%}
|
|
88
|
+
{%- assign target_is_code = true -%}
|
|
89
|
+
{%- endif -%}
|
|
90
|
+
{%- if first_char == "-" or target contains "{" or target contains "}" or target contains "(" or target contains ")" or target contains '"' or target contains "'" -%}
|
|
91
|
+
{%- assign target_is_code = true -%}
|
|
92
|
+
{%- endif -%}
|
|
93
|
+
{%- if target != "" and target.size < 200 and target_is_code == false -%}
|
|
56
94
|
{%- assign outgoing = outgoing | push: target -%}
|
|
57
95
|
{%- endif -%}
|
|
58
96
|
{%- endif -%}
|
|
@@ -65,7 +103,7 @@ sitemap: false
|
|
|
65
103
|
"collection": {{ doc.collection | default: nil | jsonify }},
|
|
66
104
|
"tags": {{ doc.tags | default: empty | jsonify }},
|
|
67
105
|
"categories": {{ doc.categories | default: empty | jsonify }},
|
|
68
|
-
"aliases": {{
|
|
106
|
+
"aliases": {{ aliases | jsonify }},
|
|
69
107
|
"outgoing": {{ outgoing | jsonify }},
|
|
70
108
|
"excerpt": {{ body_excerpt | jsonify }}
|
|
71
109
|
}{% unless forloop.last %},{% endunless %}
|