jekyll-theme-zer0 1.2.1 → 1.3.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 +4 -4
- data/CHANGELOG.md +38 -0
- data/README.md +28 -4
- data/_includes/components/js-cdn.html +5 -1
- data/_includes/content/backlinks.html +103 -0
- data/_includes/content/transclude.html +62 -0
- data/_includes/navigation/local-graph.html +44 -0
- data/_includes/navigation/sidebar-left.html +6 -0
- data/_layouts/default.html +9 -0
- data/_layouts/note.html +7 -1
- data/_plugins/obsidian_links.rb +427 -0
- data/_sass/core/_obsidian.scss +169 -0
- data/_sass/custom.scss +3 -0
- data/assets/data/wiki-index.json +74 -0
- data/assets/js/obsidian-graph.js +475 -0
- data/assets/js/obsidian-local-graph.js +382 -0
- data/assets/js/obsidian-wiki-links.js +341 -0
- data/scripts/lint-pages +6 -0
- metadata +11 -2
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* obsidian-wiki-links.js
|
|
3
|
+
*
|
|
4
|
+
* Client-side fallback that resolves Obsidian-style wiki-links / embeds /
|
|
5
|
+
* inline tags on the rendered page. Used when the Jekyll plugin
|
|
6
|
+
* (_plugins/obsidian_links.rb) is NOT available — most importantly the
|
|
7
|
+
* default GitHub Pages remote_theme build, which only allows whitelisted
|
|
8
|
+
* Jekyll plugins.
|
|
9
|
+
*
|
|
10
|
+
* The plugin (when it runs) emits ready HTML and this script becomes a
|
|
11
|
+
* no-op for already-rewritten content. When the plugin is absent, the raw
|
|
12
|
+
* `[[Page]]` / `![[image.png]]` syntax survives kramdown and we rewrite it
|
|
13
|
+
* here in the DOM using assets/data/wiki-index.json.
|
|
14
|
+
*
|
|
15
|
+
* Lookup keys are normalized identically to the Ruby plugin:
|
|
16
|
+
* value.toLowerCase().trim().replace(/\s+/g, ' ')
|
|
17
|
+
*
|
|
18
|
+
* Loaded from _includes/components/js-cdn.html (deferred). Skips when
|
|
19
|
+
* `window.__OBSIDIAN_DISABLE_CLIENT__` is true (set this in dev to debug
|
|
20
|
+
* the server-side plugin output).
|
|
21
|
+
*/
|
|
22
|
+
(function () {
|
|
23
|
+
'use strict';
|
|
24
|
+
|
|
25
|
+
if (window.__OBSIDIAN_DISABLE_CLIENT__) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
var CONFIG = {
|
|
30
|
+
indexUrl: (window.OBSIDIAN_WIKI_INDEX_URL ||
|
|
31
|
+
((document.querySelector('base') || {}).href || '/') + 'assets/data/wiki-index.json'),
|
|
32
|
+
attachmentsPath: window.OBSIDIAN_ATTACHMENTS_PATH || '/assets/images/notes',
|
|
33
|
+
tagBase: window.OBSIDIAN_TAG_BASE || '/tags/',
|
|
34
|
+
wikiLinkClass: 'wiki-link',
|
|
35
|
+
brokenLinkClass: 'wiki-link wiki-link-broken'
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
var IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.avif', '.bmp'];
|
|
39
|
+
|
|
40
|
+
// Patterns kept conservative: we only mutate text nodes inside the main
|
|
41
|
+
// content container so navigation chrome / code samples stay untouched.
|
|
42
|
+
var EMBED_RE = /!\[\[([^\]\n|]+?)(?:\|([^\]\n]+))?\]\]/g;
|
|
43
|
+
var LINK_RE = /\[\[([^\]\n|]+?)(?:\|([^\]\n]+))?\]\]/g;
|
|
44
|
+
var TAG_RE = /(^|[^\w/#&])#([A-Za-z][\w/-]{0,63})/g;
|
|
45
|
+
|
|
46
|
+
function normalize(value) {
|
|
47
|
+
return String(value || '').toLowerCase().trim().replace(/\s+/g, ' ');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function escapeHtml(value) {
|
|
51
|
+
return String(value == null ? '' : value)
|
|
52
|
+
.replace(/&/g, '&')
|
|
53
|
+
.replace(/</g, '<')
|
|
54
|
+
.replace(/>/g, '>')
|
|
55
|
+
.replace(/"/g, '"')
|
|
56
|
+
.replace(/'/g, ''');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function splitAnchor(target) {
|
|
60
|
+
var blockMatch = target.match(/^(.+?)\^([\w-]+)$/);
|
|
61
|
+
if (blockMatch) return { page: blockMatch[1].trim(), anchor: blockMatch[2].trim() };
|
|
62
|
+
var headingMatch = target.match(/^(.+?)#(.+)$/);
|
|
63
|
+
if (headingMatch) return { page: headingMatch[1].trim(), anchor: headingMatch[2].trim() };
|
|
64
|
+
return { page: target.trim(), anchor: null };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function buildIndex(payload) {
|
|
68
|
+
var byKey = Object.create(null);
|
|
69
|
+
if (!payload || !Array.isArray(payload.entries)) return byKey;
|
|
70
|
+
|
|
71
|
+
payload.entries.forEach(function (entry) {
|
|
72
|
+
if (!entry || !entry.url) return;
|
|
73
|
+
var keys = [];
|
|
74
|
+
if (entry.title) keys.push(entry.title);
|
|
75
|
+
if (entry.basename) keys.push(entry.basename);
|
|
76
|
+
(entry.aliases || []).forEach(function (a) { if (a) keys.push(a); });
|
|
77
|
+
keys.forEach(function (k) {
|
|
78
|
+
var nk = normalize(k);
|
|
79
|
+
if (!nk || byKey[nk]) return; // first wins, mirrors plugin behaviour
|
|
80
|
+
byKey[nk] = entry;
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
return byKey;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function renderImageEmbed(target, modifier) {
|
|
87
|
+
var widthAttr = '';
|
|
88
|
+
var alt = target;
|
|
89
|
+
if (modifier) {
|
|
90
|
+
if (/^\d+$/.test(modifier)) widthAttr = ' width="' + modifier + '"';
|
|
91
|
+
else alt = modifier;
|
|
92
|
+
}
|
|
93
|
+
var src = target.charAt(0) === '/' ? target : (CONFIG.attachmentsPath.replace(/\/$/, '') + '/' + target);
|
|
94
|
+
return '<img src="' + escapeHtml(src) + '" alt="' + escapeHtml(alt) +
|
|
95
|
+
'" loading="lazy" class="obsidian-embed obsidian-embed-image"' + widthAttr + ' />';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function renderNoteEmbed(target, byKey) {
|
|
99
|
+
var parts = splitAnchor(target);
|
|
100
|
+
var info = byKey[normalize(parts.page)];
|
|
101
|
+
if (!info) {
|
|
102
|
+
return '<div class="obsidian-embed obsidian-embed-broken alert alert-warning" role="alert">' +
|
|
103
|
+
'Embed not found: <code>' + escapeHtml(target) + '</code></div>';
|
|
104
|
+
}
|
|
105
|
+
var url = info.url + (parts.anchor ? '#' + parts.anchor.toLowerCase().replace(/[^\w\s-]/g, '').replace(/\s+/g, '-') : '');
|
|
106
|
+
return '<div class="obsidian-embed obsidian-embed-note">' +
|
|
107
|
+
'<div class="obsidian-embed-header"><a href="' + escapeHtml(url) + '">' +
|
|
108
|
+
escapeHtml(info.title || parts.page) + '</a></div>' +
|
|
109
|
+
'<div class="obsidian-embed-excerpt">' + escapeHtml(info.excerpt || '') + '</div>' +
|
|
110
|
+
'</div>';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function renderWikiLink(target, aliasText, byKey, currentUrl) {
|
|
114
|
+
var parts = splitAnchor(target);
|
|
115
|
+
var display = aliasText || (parts.anchor ? parts.page + ' \u203A ' + parts.anchor : parts.page);
|
|
116
|
+
var info = byKey[normalize(parts.page)];
|
|
117
|
+
if (!info) {
|
|
118
|
+
return '<a href="#" class="' + CONFIG.brokenLinkClass +
|
|
119
|
+
'" data-wiki-target="' + escapeHtml(parts.page) +
|
|
120
|
+
'" title="Unresolved wiki-link: ' + escapeHtml(parts.page) + '">' +
|
|
121
|
+
escapeHtml(display) + '</a>';
|
|
122
|
+
}
|
|
123
|
+
var url = info.url + (parts.anchor ? '#' + parts.anchor.toLowerCase().replace(/[^\w\s-]/g, '').replace(/\s+/g, '-') : '');
|
|
124
|
+
var currentAttr = currentUrl && info.url === currentUrl ? ' aria-current="page"' : '';
|
|
125
|
+
return '<a href="' + escapeHtml(url) + '" class="' + CONFIG.wikiLinkClass +
|
|
126
|
+
'" data-wiki-target="' + escapeHtml(parts.page) + '"' + currentAttr + '>' +
|
|
127
|
+
escapeHtml(display) + '</a>';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function rewriteHtml(html, byKey, currentUrl) {
|
|
131
|
+
// Embeds first (longer match `![[ … ]]` would otherwise be eaten by [[ … ]]).
|
|
132
|
+
html = html.replace(EMBED_RE, function (_match, target, modifier) {
|
|
133
|
+
target = target.trim();
|
|
134
|
+
modifier = (modifier || '').trim();
|
|
135
|
+
var ext = (target.match(/\.[A-Za-z0-9]+$/) || [''])[0].toLowerCase();
|
|
136
|
+
if (IMAGE_EXTENSIONS.indexOf(ext) !== -1) return renderImageEmbed(target, modifier);
|
|
137
|
+
return renderNoteEmbed(target, byKey);
|
|
138
|
+
});
|
|
139
|
+
html = html.replace(LINK_RE, function (_match, target, alias) {
|
|
140
|
+
return renderWikiLink(target.trim(), (alias || '').trim(), byKey, currentUrl);
|
|
141
|
+
});
|
|
142
|
+
html = html.replace(TAG_RE, function (_match, lead, tag) {
|
|
143
|
+
var url = (CONFIG.tagBase.replace(/\/$/, '') + '/#' + tag.toLowerCase().replace(/\//g, '-'));
|
|
144
|
+
return lead + '<a href="' + escapeHtml(url) + '" class="obsidian-tag">#' + escapeHtml(tag) + '</a>';
|
|
145
|
+
});
|
|
146
|
+
return html;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function eligibleNode(node) {
|
|
150
|
+
// Only walk text nodes whose parent isn't code / pre / a / script / style
|
|
151
|
+
// and that contain at least one of our markers.
|
|
152
|
+
if (node.nodeType !== Node.TEXT_NODE) return false;
|
|
153
|
+
var parent = node.parentNode;
|
|
154
|
+
while (parent && parent !== document.body) {
|
|
155
|
+
var tag = parent.nodeName;
|
|
156
|
+
if (tag === 'CODE' || tag === 'PRE' || tag === 'A' || tag === 'SCRIPT' || tag === 'STYLE') return false;
|
|
157
|
+
if (parent.classList && (parent.classList.contains('mermaid') || parent.classList.contains('obsidian-embed'))) return false;
|
|
158
|
+
parent = parent.parentNode;
|
|
159
|
+
}
|
|
160
|
+
var text = node.nodeValue;
|
|
161
|
+
return text && (text.indexOf('[[') !== -1 || text.indexOf('![[') !== -1 || /(^|[^\w/#&])#[A-Za-z]/.test(text));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function rewriteContainer(container, byKey, currentUrl) {
|
|
165
|
+
if (!container) return 0;
|
|
166
|
+
var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, null);
|
|
167
|
+
var batch = [];
|
|
168
|
+
var node;
|
|
169
|
+
while ((node = walker.nextNode())) {
|
|
170
|
+
if (eligibleNode(node)) batch.push(node);
|
|
171
|
+
}
|
|
172
|
+
var rewrites = 0;
|
|
173
|
+
batch.forEach(function (textNode) {
|
|
174
|
+
var original = textNode.nodeValue;
|
|
175
|
+
var rewritten = rewriteHtml(original, byKey, currentUrl);
|
|
176
|
+
if (rewritten === original) return;
|
|
177
|
+
|
|
178
|
+
// The rewriter only inserts HTML for matched markers; everything else
|
|
179
|
+
// is the raw text-node content. Wrap the bits between matches in
|
|
180
|
+
// escaped fragments so injected `<` / `&` from the source stays inert.
|
|
181
|
+
var safe = '';
|
|
182
|
+
var lastIndex = 0;
|
|
183
|
+
var combined = /(!\[\[[^\]\n|]+(?:\|[^\]\n]+)?\]\])|(\[\[[^\]\n|]+(?:\|[^\]\n]+)?\]\])|((?:^|[^\w/#&])#[A-Za-z][\w/-]{0,63})/g;
|
|
184
|
+
var match;
|
|
185
|
+
while ((match = combined.exec(original)) !== null) {
|
|
186
|
+
safe += escapeHtml(original.slice(lastIndex, match.index));
|
|
187
|
+
safe += rewriteHtml(match[0], byKey, currentUrl);
|
|
188
|
+
lastIndex = combined.lastIndex;
|
|
189
|
+
}
|
|
190
|
+
safe += escapeHtml(original.slice(lastIndex));
|
|
191
|
+
|
|
192
|
+
var template = document.createElement('template');
|
|
193
|
+
template.innerHTML = safe;
|
|
194
|
+
textNode.parentNode.replaceChild(template.content, textNode);
|
|
195
|
+
rewrites += 1;
|
|
196
|
+
});
|
|
197
|
+
return rewrites;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function getCurrentUrl() {
|
|
201
|
+
return window.location.pathname;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ---- Callouts (DOM-level, post-kramdown) -----------------------------
|
|
205
|
+
// Kramdown turns `> [!type] Title\n> body` into:
|
|
206
|
+
// <blockquote><p>[!type] Title\nbody</p></blockquote>
|
|
207
|
+
// We detect that pattern and rewrite the blockquote into a Bootstrap alert.
|
|
208
|
+
var CALLOUT_TYPES = {
|
|
209
|
+
note: { alert: 'primary', icon: 'bi-pencil-square' },
|
|
210
|
+
abstract: { alert: 'secondary', icon: 'bi-card-text' },
|
|
211
|
+
summary: { alert: 'secondary', icon: 'bi-card-text' },
|
|
212
|
+
tldr: { alert: 'secondary', icon: 'bi-card-text' },
|
|
213
|
+
info: { alert: 'info', icon: 'bi-info-circle' },
|
|
214
|
+
todo: { alert: 'info', icon: 'bi-check2-square' },
|
|
215
|
+
tip: { alert: 'success', icon: 'bi-lightbulb' },
|
|
216
|
+
hint: { alert: 'success', icon: 'bi-lightbulb' },
|
|
217
|
+
important: { alert: 'warning', icon: 'bi-exclamation-circle' },
|
|
218
|
+
success: { alert: 'success', icon: 'bi-check-circle' },
|
|
219
|
+
check: { alert: 'success', icon: 'bi-check-circle' },
|
|
220
|
+
done: { alert: 'success', icon: 'bi-check-circle' },
|
|
221
|
+
question: { alert: 'info', icon: 'bi-question-circle' },
|
|
222
|
+
help: { alert: 'info', icon: 'bi-question-circle' },
|
|
223
|
+
faq: { alert: 'info', icon: 'bi-question-circle' },
|
|
224
|
+
warning: { alert: 'warning', icon: 'bi-exclamation-triangle' },
|
|
225
|
+
caution: { alert: 'warning', icon: 'bi-exclamation-triangle' },
|
|
226
|
+
attention: { alert: 'warning', icon: 'bi-exclamation-triangle' },
|
|
227
|
+
failure: { alert: 'danger', icon: 'bi-x-octagon' },
|
|
228
|
+
fail: { alert: 'danger', icon: 'bi-x-octagon' },
|
|
229
|
+
missing: { alert: 'danger', icon: 'bi-x-octagon' },
|
|
230
|
+
danger: { alert: 'danger', icon: 'bi-shield-exclamation' },
|
|
231
|
+
error: { alert: 'danger', icon: 'bi-shield-exclamation' },
|
|
232
|
+
bug: { alert: 'danger', icon: 'bi-bug' },
|
|
233
|
+
example: { alert: 'secondary', icon: 'bi-code-slash' },
|
|
234
|
+
quote: { alert: 'secondary', icon: 'bi-chat-quote' },
|
|
235
|
+
cite: { alert: 'secondary', icon: 'bi-chat-quote' }
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// Match the first line of the first <p> inside a blockquote, e.g.
|
|
239
|
+
// `[!warning]+ Foldable warning`.
|
|
240
|
+
var CALLOUT_HEAD_RE = /^\s*\[!([A-Za-z]+)\]([+-]?)\s*([^\n]*)/;
|
|
241
|
+
|
|
242
|
+
function rewriteCallouts(container) {
|
|
243
|
+
if (!container) return 0;
|
|
244
|
+
var quotes = container.querySelectorAll('blockquote');
|
|
245
|
+
var count = 0;
|
|
246
|
+
quotes.forEach(function (bq) {
|
|
247
|
+
if (bq.dataset.obsidianCallout) return; // already processed
|
|
248
|
+
var firstChild = bq.firstElementChild;
|
|
249
|
+
// Walk past whitespace text nodes
|
|
250
|
+
while (firstChild && firstChild.nodeName !== 'P' && firstChild.nodeType !== 1) {
|
|
251
|
+
firstChild = firstChild.nextElementSibling;
|
|
252
|
+
}
|
|
253
|
+
if (!firstChild || firstChild.nodeName !== 'P') return;
|
|
254
|
+
|
|
255
|
+
var rawText = firstChild.textContent || '';
|
|
256
|
+
var m = rawText.match(CALLOUT_HEAD_RE);
|
|
257
|
+
if (!m) return;
|
|
258
|
+
|
|
259
|
+
var type = m[1].toLowerCase();
|
|
260
|
+
var spec = CALLOUT_TYPES[type] || CALLOUT_TYPES.note;
|
|
261
|
+
var fold = m[2];
|
|
262
|
+
var titleText = (m[3] || '').trim() || (type.charAt(0).toUpperCase() + type.slice(1));
|
|
263
|
+
|
|
264
|
+
// Strip the "[!type]…" head from the first paragraph (keep any trailing text)
|
|
265
|
+
var headLength = m[0].length;
|
|
266
|
+
var trailing = rawText.slice(headLength).replace(/^\s*\n?/, '');
|
|
267
|
+
if (trailing) {
|
|
268
|
+
firstChild.textContent = trailing;
|
|
269
|
+
} else {
|
|
270
|
+
firstChild.remove();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
var wrapper = document.createElement('div');
|
|
274
|
+
wrapper.className = 'alert alert-' + spec.alert + ' obsidian-callout obsidian-callout-' + type;
|
|
275
|
+
wrapper.setAttribute('role', 'alert');
|
|
276
|
+
wrapper.dataset.obsidianCallout = type;
|
|
277
|
+
if (fold === '-') wrapper.dataset.collapsed = 'true';
|
|
278
|
+
|
|
279
|
+
var titleEl = document.createElement('div');
|
|
280
|
+
titleEl.className = 'obsidian-callout-title';
|
|
281
|
+
titleEl.innerHTML = '<i class="bi ' + spec.icon + ' me-2" aria-hidden="true"></i>' + escapeHtml(titleText);
|
|
282
|
+
wrapper.appendChild(titleEl);
|
|
283
|
+
|
|
284
|
+
var bodyEl = document.createElement('div');
|
|
285
|
+
bodyEl.className = 'obsidian-callout-body';
|
|
286
|
+
// Move blockquote children into the body, preserving inner HTML
|
|
287
|
+
while (bq.firstChild) {
|
|
288
|
+
bodyEl.appendChild(bq.firstChild);
|
|
289
|
+
}
|
|
290
|
+
wrapper.appendChild(bodyEl);
|
|
291
|
+
|
|
292
|
+
bq.parentNode.replaceChild(wrapper, bq);
|
|
293
|
+
count += 1;
|
|
294
|
+
});
|
|
295
|
+
return count;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function init() {
|
|
299
|
+
var container = document.querySelector('#main-content, .bd-content, main, article') || document.body;
|
|
300
|
+
if (!container) return;
|
|
301
|
+
|
|
302
|
+
fetch(CONFIG.indexUrl, { credentials: 'same-origin', cache: 'force-cache' })
|
|
303
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
304
|
+
.then(function (payload) {
|
|
305
|
+
if (!payload) return;
|
|
306
|
+
var byKey = buildIndex(payload);
|
|
307
|
+
window.__OBSIDIAN_INDEX__ = byKey;
|
|
308
|
+
var rewrites = rewriteContainer(container, byKey, getCurrentUrl());
|
|
309
|
+
var calloutCount = rewriteCallouts(container);
|
|
310
|
+
if ((rewrites > 0 || calloutCount > 0) && window.console && console.debug) {
|
|
311
|
+
console.debug('[obsidian] rewrote', rewrites, 'text nodes,', calloutCount, 'callouts');
|
|
312
|
+
}
|
|
313
|
+
document.dispatchEvent(new CustomEvent('obsidian:ready', { detail: { count: payload.count || 0, calloutCount: calloutCount } }));
|
|
314
|
+
})
|
|
315
|
+
.catch(function (err) {
|
|
316
|
+
// Even if the index fails, we can still convert callouts (no index needed).
|
|
317
|
+
try {
|
|
318
|
+
var calloutCount = rewriteCallouts(container);
|
|
319
|
+
if (calloutCount > 0 && window.console && console.debug) {
|
|
320
|
+
console.debug('[obsidian] rewrote', calloutCount, 'callouts (index unavailable)');
|
|
321
|
+
}
|
|
322
|
+
} catch (e) { /* swallow */ }
|
|
323
|
+
if (window.console && console.warn) console.warn('[obsidian] wiki-index fetch failed:', err);
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Expose for testing / programmatic use
|
|
328
|
+
window.ObsidianResolver = {
|
|
329
|
+
rewriteHtml: rewriteHtml,
|
|
330
|
+
rewriteCallouts: rewriteCallouts,
|
|
331
|
+
rewriteContainer: rewriteContainer,
|
|
332
|
+
buildIndex: buildIndex,
|
|
333
|
+
normalize: normalize
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
if (document.readyState === 'loading') {
|
|
337
|
+
document.addEventListener('DOMContentLoaded', init);
|
|
338
|
+
} else {
|
|
339
|
+
init();
|
|
340
|
+
}
|
|
341
|
+
})();
|
data/scripts/lint-pages
CHANGED
|
@@ -400,6 +400,12 @@ scan_collection() {
|
|
|
400
400
|
|
|
401
401
|
if [[ -d "$REPO_ROOT/$dir_part" ]]; then
|
|
402
402
|
while IFS= read -r -d '' filepath; do
|
|
403
|
+
# Skip Obsidian/Jekyll template directories — these contain
|
|
404
|
+
# placeholder front matter ({{title}}, {{date}}) that is never
|
|
405
|
+
# rendered as a real page.
|
|
406
|
+
case "$filepath" in
|
|
407
|
+
*/_templates/*) continue ;;
|
|
408
|
+
esac
|
|
403
409
|
validate_file "$filepath" "$collection"
|
|
404
410
|
files_found=$((files_found + 1))
|
|
405
411
|
# shellcheck disable=SC2086
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jekyll-theme-zer0
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Amr Abdel
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04-
|
|
11
|
+
date: 2026-04-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: jekyll
|
|
@@ -142,6 +142,7 @@ files:
|
|
|
142
142
|
- _includes/components/theme-customizer.html
|
|
143
143
|
- _includes/components/theme-info.html
|
|
144
144
|
- _includes/components/zer0-env-var.html
|
|
145
|
+
- _includes/content/backlinks.html
|
|
145
146
|
- _includes/content/giscus.html
|
|
146
147
|
- _includes/content/intro.html
|
|
147
148
|
- _includes/content/jsonld-faq.html
|
|
@@ -149,6 +150,7 @@ files:
|
|
|
149
150
|
- _includes/content/seo.html
|
|
150
151
|
- _includes/content/sitemap.html
|
|
151
152
|
- _includes/content/toc.html
|
|
153
|
+
- _includes/content/transclude.html
|
|
152
154
|
- _includes/core/branding.html
|
|
153
155
|
- _includes/core/footer.html
|
|
154
156
|
- _includes/core/head.html
|
|
@@ -158,6 +160,7 @@ files:
|
|
|
158
160
|
- _includes/landing/landing-quick-links.html
|
|
159
161
|
- _includes/navigation/admin-nav.html
|
|
160
162
|
- _includes/navigation/breadcrumbs.html
|
|
163
|
+
- _includes/navigation/local-graph.html
|
|
161
164
|
- _includes/navigation/nav-tree.html
|
|
162
165
|
- _includes/navigation/nav_list.html
|
|
163
166
|
- _includes/navigation/navbar.html
|
|
@@ -194,11 +197,13 @@ files:
|
|
|
194
197
|
- _layouts/stats.html
|
|
195
198
|
- _layouts/tag.html
|
|
196
199
|
- _layouts/welcome.html
|
|
200
|
+
- _plugins/obsidian_links.rb
|
|
197
201
|
- _plugins/preview_image_generator.rb
|
|
198
202
|
- _plugins/theme_version.rb
|
|
199
203
|
- _sass/core/_docs-layout.scss
|
|
200
204
|
- _sass/core/_nav-tree.scss
|
|
201
205
|
- _sass/core/_navbar.scss
|
|
206
|
+
- _sass/core/_obsidian.scss
|
|
202
207
|
- _sass/core/_offcanvas-panels.scss
|
|
203
208
|
- _sass/core/_syntax.scss
|
|
204
209
|
- _sass/core/_theme.scss
|
|
@@ -247,6 +252,7 @@ files:
|
|
|
247
252
|
- assets/data/notebooks/sales_data.csv
|
|
248
253
|
- assets/data/notebooks/survey_responses.csv
|
|
249
254
|
- assets/data/notebooks/weather_data.csv
|
|
255
|
+
- assets/data/wiki-index.json
|
|
250
256
|
- assets/images/authors/bamr87.png
|
|
251
257
|
- assets/images/favicon_gpt_computer_retro.png
|
|
252
258
|
- assets/images/gravatar-small.png
|
|
@@ -298,6 +304,9 @@ files:
|
|
|
298
304
|
- assets/js/nanobar.min.js
|
|
299
305
|
- assets/js/nav-editor.js
|
|
300
306
|
- assets/js/navigation.js
|
|
307
|
+
- assets/js/obsidian-graph.js
|
|
308
|
+
- assets/js/obsidian-local-graph.js
|
|
309
|
+
- assets/js/obsidian-wiki-links.js
|
|
301
310
|
- assets/js/palette-generator.js
|
|
302
311
|
- assets/js/particles-source.js
|
|
303
312
|
- assets/js/particles.js
|