jekyll-obsidian 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.standard.yml +3 -0
- data/CHANGELOG.md +49 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/LICENSE +21 -0
- data/README.md +97 -0
- data/Rakefile +10 -0
- data/assets/css/obsidian.scss +103 -0
- data/assets/css/partials/_canvas.scss +62 -0
- data/assets/css/partials/_explorer.scss +128 -0
- data/assets/css/partials/_fileread.scss +111 -0
- data/assets/css/partials/_loading.scss +28 -0
- data/assets/css/partials/_modals.scss +206 -0
- data/assets/css/partials/_note.scss +113 -0
- data/assets/css/partials/_sidebar.scss +30 -0
- data/assets/includes/canvas.html +403 -0
- data/assets/includes/explorer.html +202 -0
- data/assets/includes/fileread.html +338 -0
- data/assets/includes/modals.html +277 -0
- data/assets/includes/note.html +175 -0
- data/assets/includes/sidebar.html +106 -0
- data/assets/layouts/obsidian.html +294 -0
- data/lib/jekyll/obsidian/version.rb +7 -0
- data/lib/jekyll/obsidian.rb +250 -0
- data/lib/jekyll/remote.rb +36 -0
- data/lib/jekyll-obsidian.rb +3 -0
- data/screenshots/jekyll-obsidian.png +0 -0
- data/sig/jekyll/obsidian.rbs +6 -0
- metadata +103 -0
@@ -0,0 +1,338 @@
|
|
1
|
+
<head>
|
2
|
+
<script src="https://cdn.jsdelivr.net/npm/markdown-it/dist/markdown-it.min.js"></script>
|
3
|
+
</head>
|
4
|
+
|
5
|
+
<!-- --------------------------- Data Extractors --------------------------- -->
|
6
|
+
<script>
|
7
|
+
function extractFilenameFromPath(filePath, removeExtension = true) {
|
8
|
+
const fileName = filePath.split('/').pop();
|
9
|
+
return removeExtension ? fileName.replace(/\.[^/.]+$/, "") : fileName;
|
10
|
+
}
|
11
|
+
function extractBacklinks(content) {
|
12
|
+
// Matches double brackets not preceded by an exclamation mark: (?<!\!)\[\[([^\]]+)\]\]
|
13
|
+
return content.match(/(?<!\!)\[\[([^\]]+)\]\]/g);
|
14
|
+
}
|
15
|
+
|
16
|
+
function extractEmbeds(content) {
|
17
|
+
// !\[\[([^\]]+)\]\]: Matches double brackets prefixed by an exclamation mark.
|
18
|
+
return content.match(/!\[\[([^\]]+)\]\]/g);
|
19
|
+
}
|
20
|
+
|
21
|
+
function extractCallouts(content) {
|
22
|
+
// ^\>: Ensures the match starts at the beginning of a line with a >.
|
23
|
+
// \[\![^\]]+\]: Matches a space, followed by ![ w/ content inside the square brackets.
|
24
|
+
return content.match(/^\> \[\![^\]]+\]/gm);
|
25
|
+
}
|
26
|
+
</script>
|
27
|
+
|
28
|
+
<!-- --------------------------- Data Replacers ---------------------------- -->
|
29
|
+
<script>
|
30
|
+
function replaceBacklinks(backlinksArr, cleanedHTML, backlinks_enabled = "true") {
|
31
|
+
if (backlinksArr) backlinksArr.forEach(backlink => {
|
32
|
+
const innerText = backlink.slice(2, -2);
|
33
|
+
let backlinkElem;
|
34
|
+
if (backlinks_enabled === "true") {
|
35
|
+
const button = document.createElement('button');
|
36
|
+
backlinkElem = button;
|
37
|
+
} else {
|
38
|
+
const span = document.createElement('span');
|
39
|
+
backlinkElem = span;
|
40
|
+
}
|
41
|
+
let parts = innerText.split('|');
|
42
|
+
if (parts.length > 1) {
|
43
|
+
backlinkElem.textContent =
|
44
|
+
extractFilenameFromPath(parts[1].trim(), false);
|
45
|
+
|
46
|
+
backlinkElem.dataset.value = parts[0].trim();
|
47
|
+
|
48
|
+
} else {
|
49
|
+
backlinkElem.textContent = innerText;
|
50
|
+
}
|
51
|
+
|
52
|
+
backlinkElem.className = 'backlink';
|
53
|
+
cleanedHTML.innerHTML =
|
54
|
+
cleanedHTML.innerHTML.replace(backlink, backlinkElem.outerHTML);
|
55
|
+
});
|
56
|
+
}
|
57
|
+
</script>
|
58
|
+
|
59
|
+
<!-- ---------------------- Parse string to Markdown ----------------------- -->
|
60
|
+
<script>
|
61
|
+
function parseStringToMarkdown(content) {
|
62
|
+
const md = window.markdownit({ breaks: true });
|
63
|
+
let htmlContent = md.render(content);
|
64
|
+
|
65
|
+
const markdownDiv = document.createElement('div');
|
66
|
+
markdownDiv.innerHTML = DOMPurify.sanitize(htmlContent);
|
67
|
+
|
68
|
+
const backlinksArr = extractBacklinks(content);
|
69
|
+
if (backlinksArr)
|
70
|
+
replaceBacklinks(backlinksArr, markdownDiv);
|
71
|
+
const calloutsArr = extractCallouts(content);
|
72
|
+
if (calloutsArr)
|
73
|
+
generateCallouts(calloutsArr, markdownDiv);
|
74
|
+
const embedsArr = extractEmbeds(content);
|
75
|
+
if (embedsArr) generateEmbeds(embedsArr, markdownDiv);
|
76
|
+
return markdownDiv
|
77
|
+
}
|
78
|
+
</script>
|
79
|
+
|
80
|
+
<!-- --------------------------- Data Generators --------------------------- -->
|
81
|
+
<script>
|
82
|
+
function generateBreadcrumbs(filePath) {
|
83
|
+
const filePathArr = filePath.split('/').filter(Boolean);
|
84
|
+
const breadcrumbs = FILEREAD.querySelector('#breadcrumb');
|
85
|
+
while (breadcrumbs.firstChild)
|
86
|
+
breadcrumbs.removeChild(breadcrumbs.firstChild);
|
87
|
+
filePathArr.forEach((path, index) => {
|
88
|
+
const li = document.createElement('li');
|
89
|
+
breadcrumbs.appendChild(li);
|
90
|
+
const btn = document.createElement('button');
|
91
|
+
btn.textContent = path;
|
92
|
+
li.appendChild(btn);
|
93
|
+
if (index === filePathArr.length - 1) {
|
94
|
+
btn.textContent = extractFilenameFromPath(path);
|
95
|
+
btn.addEventListener('click', () => {
|
96
|
+
if (filePath.endsWith('.mdnote'))
|
97
|
+
FILEREAD.scrollTop = 0;
|
98
|
+
else if (filePath.endsWith('.canvas'))
|
99
|
+
resetCanvasPosition();
|
100
|
+
});
|
101
|
+
}
|
102
|
+
});
|
103
|
+
}
|
104
|
+
|
105
|
+
function mapBacklinksToJson(backlinks_json) {
|
106
|
+
FILEREAD.querySelectorAll('.backlink').forEach(button => {
|
107
|
+
button.addEventListener('click', (event) => {
|
108
|
+
for (const parsedFilePath in backlinks_json) {
|
109
|
+
let _filePath = parsedFilePath.replace("/:|", "'");
|
110
|
+
if (_filePath.endsWith('/')) _filePath = _filePath.slice(0, -1);
|
111
|
+
const fileName = extractFilenameFromPath(_filePath);
|
112
|
+
if (!button.dataset.value) {
|
113
|
+
const dataValue = event.target.textContent;
|
114
|
+
if (dataValue.includes('#')) {
|
115
|
+
const parts = dataValue.split('#');
|
116
|
+
if (fileName === parts[0])
|
117
|
+
dispatchFileSelectEvt('/' + _filePath, parts[1]);
|
118
|
+
} else {
|
119
|
+
if (fileName === dataValue)
|
120
|
+
dispatchFileSelectEvt(_filePath);
|
121
|
+
}
|
122
|
+
} else {
|
123
|
+
const dataValue = button.dataset.value;
|
124
|
+
if (dataValue.includes('#')) {
|
125
|
+
const parts = dataValue.split('#');
|
126
|
+
if (fileName === parts[0])
|
127
|
+
dispatchFileSelectEvt('/' + _filePath, parts[1]);
|
128
|
+
} else {
|
129
|
+
if (fileName === extractFilenameFromPath(dataValue)) {
|
130
|
+
dispatchFileSelectEvt('/' + _filePath);
|
131
|
+
}
|
132
|
+
}
|
133
|
+
}
|
134
|
+
}
|
135
|
+
});
|
136
|
+
});
|
137
|
+
}
|
138
|
+
|
139
|
+
function removeExtraTrailingDotFromFilename(encodedFileName) {
|
140
|
+
const lastDotIndex = encodedFileName.lastIndexOf('.');
|
141
|
+
if (lastDotIndex > 0 && encodedFileName.charAt(lastDotIndex - 1) === '.')
|
142
|
+
return encodedFileName.substring(0, lastDotIndex - 1) + encodedFileName.substring(lastDotIndex);
|
143
|
+
return encodedFileName;
|
144
|
+
}
|
145
|
+
|
146
|
+
|
147
|
+
function generateCallouts(calloutsArr, cleanedHTML) {
|
148
|
+
calloutsArr.forEach(callout => {
|
149
|
+
// Extract the callout type including square brackets, removing "> " part
|
150
|
+
const calloutType = callout.slice(2).trim().toLowerCase();
|
151
|
+
console.log("calloutType: " + calloutType);
|
152
|
+
let svgString = '';
|
153
|
+
switch (calloutType) {
|
154
|
+
case '[!note]':
|
155
|
+
svgString = getSVGIcon(Icon.Note);
|
156
|
+
break;
|
157
|
+
case '[!todo]':
|
158
|
+
svgString = getSVGIcon(Icon.Todo);
|
159
|
+
break;
|
160
|
+
case '[!tip]':
|
161
|
+
case '[!hint]':
|
162
|
+
case '[!important]':
|
163
|
+
svgString = getSVGIcon(Icon.Tip);
|
164
|
+
break;
|
165
|
+
case '[!question]':
|
166
|
+
case '[!help]':
|
167
|
+
case '[!faq]':
|
168
|
+
svgString = getSVGIcon(Icon.Question);
|
169
|
+
break;
|
170
|
+
case '[!success]':
|
171
|
+
case '[!check]':
|
172
|
+
case '[!done]':
|
173
|
+
svgString = getSVGIcon(Icon.Success);
|
174
|
+
break;
|
175
|
+
case '[!warning]':
|
176
|
+
case '[!caution]':
|
177
|
+
case '[!attention]':
|
178
|
+
svgString = getSVGIcon(Icon.Warning);
|
179
|
+
break;
|
180
|
+
case '[!failure]':
|
181
|
+
case '[!fail]':
|
182
|
+
case '[!missing]':
|
183
|
+
svgString = getSVGIcon(Icon.Failure);
|
184
|
+
break;
|
185
|
+
case '[!info]':
|
186
|
+
svgString = getSVGIcon(Icon.Info);
|
187
|
+
break;
|
188
|
+
case '[!example]':
|
189
|
+
svgString = getSVGIcon(Icon.Example);
|
190
|
+
break;
|
191
|
+
default:
|
192
|
+
console.log(`Unknown callout type: ${calloutType}`);
|
193
|
+
break;
|
194
|
+
}
|
195
|
+
// Perform the replacement in a case-insensitive manner
|
196
|
+
if (svgString) {
|
197
|
+
const calloutRegex = new RegExp(calloutType.replace(/[[\]!]/g, '\\$&'), 'gi');
|
198
|
+
cleanedHTML.innerHTML = cleanedHTML.innerHTML.replace(calloutRegex, svgString);
|
199
|
+
}
|
200
|
+
});
|
201
|
+
}
|
202
|
+
|
203
|
+
function generateEmbeds(embedsArr, cleanedHTML) {
|
204
|
+
const VAULTPATH = '{{ site.obsidian_vault | escape }}';
|
205
|
+
const embeds = '{{ site.data.obsidian.embeds | escape }}';
|
206
|
+
const siteurl = '{{ site.url | escape }}' + '/';
|
207
|
+
const encodedVaultPath =
|
208
|
+
VAULTPATH.split('/').map(part => encodeURIComponent(part)).join('/') + '/';
|
209
|
+
let newHTML = cleanedHTML.innerHTML;
|
210
|
+
try {
|
211
|
+
const embedsParsed = JSON.parse(embeds.replace(/"/g, '"'));
|
212
|
+
embedsArr.forEach(embed => {
|
213
|
+
const fileNameMatch = embed.match(/\[\[(.*?)\]\]/);
|
214
|
+
console.log("fileNameMatch: " + fileNameMatch);
|
215
|
+
if (fileNameMatch) {
|
216
|
+
const fileName = extractFilenameFromPath(fileNameMatch[1]);
|
217
|
+
for (let key in embedsParsed) {
|
218
|
+
const embedFileName = extractFilenameFromPath(key);
|
219
|
+
if (embedFileName.replace("/:|", "'") === fileName) {
|
220
|
+
let encodedFileName =
|
221
|
+
key.split('/').map(part => encodeURIComponent(part)).join('/');
|
222
|
+
const lastDotIndex = encodedFileName.lastIndexOf('.');
|
223
|
+
|
224
|
+
encodedFileName = removeExtraTrailingDotFromFilename(encodedFileName);
|
225
|
+
const fullEmbedPath = encodedVaultPath + encodedFileName;
|
226
|
+
const fileType = getFileType(encodedFileName);
|
227
|
+
const filePath = siteurl + fullEmbedPath;
|
228
|
+
let embedElem;
|
229
|
+
switch (fileType) {
|
230
|
+
case 'pdf':
|
231
|
+
default:
|
232
|
+
embedElem = document.createElement('a');
|
233
|
+
embedElem.href = filePath;
|
234
|
+
embedElem.className = 'url-link';
|
235
|
+
embedElem.textContent = fileName;
|
236
|
+
break;
|
237
|
+
case 'image':
|
238
|
+
embedElem = document.createElement('img');
|
239
|
+
embedElem.src = filePath;
|
240
|
+
break;
|
241
|
+
case 'audio':
|
242
|
+
embedElem = document.createElement('audio');
|
243
|
+
embedElem.src = filePath;
|
244
|
+
embedElem.controls = true;
|
245
|
+
break;
|
246
|
+
case 'video':
|
247
|
+
embedElem = document.createElement('video');
|
248
|
+
embedElem.src = filePath;
|
249
|
+
embedElem.controls = true;
|
250
|
+
break;
|
251
|
+
}
|
252
|
+
if (fileType !== 'pdf' && fileType !== 'unknown') {
|
253
|
+
embedContainer = document.createElement('div');
|
254
|
+
embedContainer.className = 'embed';
|
255
|
+
|
256
|
+
embedContainer.appendChild(embedElem);
|
257
|
+
pTag = document.createElement('p');
|
258
|
+
pTag.textContent = fileName;
|
259
|
+
embedContainer.appendChild(pTag);
|
260
|
+
newHTML = newHTML.replace(embed, embedContainer.outerHTML);
|
261
|
+
} else newHTML = newHTML.replace(embed, embedElem.outerHTML);
|
262
|
+
}
|
263
|
+
}
|
264
|
+
}
|
265
|
+
});
|
266
|
+
const tempDiv = document.createElement('div');
|
267
|
+
tempDiv.innerHTML = DOMPurify.sanitize(newHTML);
|
268
|
+
|
269
|
+
// Remove all <p> tags that are empty
|
270
|
+
tempDiv.querySelectorAll('p').forEach(p => {
|
271
|
+
if (!p.textContent.trim()) {
|
272
|
+
p.parentNode.removeChild(p);
|
273
|
+
}
|
274
|
+
});
|
275
|
+
|
276
|
+
cleanedHTML.innerHTML = tempDiv.innerHTML;
|
277
|
+
} catch (e) {
|
278
|
+
console.error("Error parsing embeds:", e);
|
279
|
+
}
|
280
|
+
}
|
281
|
+
</script>
|
282
|
+
|
283
|
+
<!-- ---------------------- File read event handlers ----------------------- -->
|
284
|
+
<script>
|
285
|
+
let BACKLINKS;
|
286
|
+
document.addEventListener('DOMContentLoaded', () => {
|
287
|
+
const homepage = '{{ site.obsidian_homepage | escape }}'
|
288
|
+
let backlinks_enabled = '{{ site.obsidian_backlinks | escape }}';
|
289
|
+
if (backlinks_enabled === null ||
|
290
|
+
backlinks_enabled === '' ||
|
291
|
+
backlinks_enabled === undefined)
|
292
|
+
backlinks_enabled = "true";
|
293
|
+
console.info("backlinks enabled? " + backlinks_enabled);
|
294
|
+
if (backlinks_enabled === "true") {
|
295
|
+
try {
|
296
|
+
BACKLINKS =
|
297
|
+
JSON.parse('{{ site.data.obsidian.backlinks | escape }}'.replace(/"/g, '"'));
|
298
|
+
console.info("backlinks parsed: " + Object.keys(BACKLINKS).length);
|
299
|
+
} catch (e) {
|
300
|
+
console.error("Error parsing backlinks:", e);
|
301
|
+
}
|
302
|
+
}
|
303
|
+
if (homepage) switchPage(homepage);
|
304
|
+
});
|
305
|
+
|
306
|
+
function switchPage(page) {
|
307
|
+
if (page.endsWith('.canvas')) {
|
308
|
+
document.getElementById('note').style.display = 'none';
|
309
|
+
document.getElementById('canvas').style.display = 'flex';
|
310
|
+
} else {
|
311
|
+
document.getElementById('canvas').style.display = 'none';
|
312
|
+
document.getElementById('note').style.display = 'flex';
|
313
|
+
}
|
314
|
+
}
|
315
|
+
|
316
|
+
function dispatchFileSelectEvt(filePath, heading) {
|
317
|
+
const EXPLORER = OBSIDIAN.querySelector('#explorer');
|
318
|
+
const detail = { filePath };
|
319
|
+
if (filePath.endsWith('.mdnote')) {
|
320
|
+
if (heading) detail.heading = heading;
|
321
|
+
const event = new CustomEvent('obsidian_noteSelect', { detail });
|
322
|
+
document.dispatchEvent(event);
|
323
|
+
Cookies.set('last-file', filePath, { expires: 300, path: '', sameSite: 'Strict' })
|
324
|
+
} else if (filePath.endsWith('.canvas')) {
|
325
|
+
const event = new CustomEvent('obsidian_canvasSelect', { detail });
|
326
|
+
document.dispatchEvent(event);
|
327
|
+
Cookies.set('last-file', filePath, { expires: 300, path: '', sameSite: 'Strict' })
|
328
|
+
} else {
|
329
|
+
const event = new CustomEvent('obsidian_mediaSelect', { detail });
|
330
|
+
document.dispatchEvent(event);
|
331
|
+
}
|
332
|
+
if (window.innerWidth < 481) {
|
333
|
+
EXPLORER.style.display = 'none';
|
334
|
+
EXPLORER.style.width = '1%';
|
335
|
+
FILEREAD.style.display = '';
|
336
|
+
}
|
337
|
+
}
|
338
|
+
</script>
|
@@ -0,0 +1,277 @@
|
|
1
|
+
<head>
|
2
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
3
|
+
</head>
|
4
|
+
|
5
|
+
<!-- -------------------------------- Modals ------------------------------- -->
|
6
|
+
<div id="modals">
|
7
|
+
<div popover id="search-modal">
|
8
|
+
<div id="header">
|
9
|
+
<form style="display: flex; flex-grow: 1;">
|
10
|
+
<input type="text" id="search-input" name="name" placeholder="Find a note..">
|
11
|
+
</form>
|
12
|
+
<button class="modal-close" popovertarget="search-modal" popovertargetaction="hide">
|
13
|
+
×
|
14
|
+
</button>
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<div popover id="info-modal">
|
19
|
+
<button class="modal-close" popovertarget="info-modal" popovertargetaction="hide">
|
20
|
+
×
|
21
|
+
</button>
|
22
|
+
<p><a href="https://github.com/khiemgluong/jekyll-obsidian" target="_blank">jekyll-obsidian</a></p>
|
23
|
+
<p>View <a href="https://obsidian.md/" target="_blank">Obsidian</a> vaults, on your <a
|
24
|
+
href="https://jekyllrb.com/" target="_blank">Jekyll</a> website.</p>
|
25
|
+
|
26
|
+
<span>Color scheme</span>
|
27
|
+
<select id="color-scheme">
|
28
|
+
<option value="system">system</option>
|
29
|
+
<option value="dark">dark</option>
|
30
|
+
<option value="light">light</option>
|
31
|
+
</select>
|
32
|
+
<input type="radio" name="color-scheme" value="system" id="systemScheme" checked hidden>
|
33
|
+
<input type="radio" name="color-scheme" value="dark" id="darkScheme" hidden>
|
34
|
+
<input type="radio" name="color-scheme" value="light" id="lightScheme" hidden>
|
35
|
+
<div id="vault-props"></div>
|
36
|
+
</div>
|
37
|
+
<div popover id="media-modal">
|
38
|
+
<div id="media"></div>
|
39
|
+
<div id="media-data"> </div>
|
40
|
+
</div>
|
41
|
+
</div>
|
42
|
+
|
43
|
+
<!-- ----------------------- Convert to byte string ------------------------ -->
|
44
|
+
<script>
|
45
|
+
function formatNumberToBytes(number) {
|
46
|
+
if (number < 1) return 'no size';
|
47
|
+
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'EB', 'ZB', 'YB'];
|
48
|
+
const digits = Math.floor((Math.log(number) / Math.log(1024)));
|
49
|
+
const unitIndex = Math.min(digits, units.length - 1); // Ensure we don't exceed the array
|
50
|
+
const sizeInUnit = number / Math.pow(1024, unitIndex);
|
51
|
+
// Format the number to have up to 3 digits
|
52
|
+
const formattedSize = sizeInUnit.toFixed(sizeInUnit >= 100 ? 0 : sizeInUnit >= 10 ? 1 : 2);
|
53
|
+
return `${formattedSize} ${units[unitIndex]}`;
|
54
|
+
}
|
55
|
+
</script>
|
56
|
+
|
57
|
+
<script>
|
58
|
+
function applyColorScheme(colorScheme) {
|
59
|
+
switch (colorScheme) {
|
60
|
+
case 'system':
|
61
|
+
MODALS.querySelector('#systemScheme').checked = true;
|
62
|
+
break;
|
63
|
+
case 'dark':
|
64
|
+
MODALS.querySelector('#darkScheme').checked = true;
|
65
|
+
break;
|
66
|
+
case 'light':
|
67
|
+
MODALS.querySelector('#lightScheme').checked = true;
|
68
|
+
break;
|
69
|
+
}
|
70
|
+
return colorScheme;
|
71
|
+
}
|
72
|
+
</script>
|
73
|
+
|
74
|
+
<!-- ----------------------- Modals event listeners ------------------------ -->
|
75
|
+
<script>
|
76
|
+
document.getElementById('search-input').addEventListener('input', function () {
|
77
|
+
console.log("search value " + this.value);
|
78
|
+
const searchModal = MODALS.querySelector('#search-modal');
|
79
|
+
const ul = searchModal.querySelector('ul');
|
80
|
+
if (ul) {
|
81
|
+
const liElements = Array.from(ul.querySelectorAll('li'));
|
82
|
+
liElements.forEach(li => {
|
83
|
+
const fileName = extractFilenameFromPath(li.innerText).toLowerCase();
|
84
|
+
console.log(li.innerText + '\t' + fileName);
|
85
|
+
if (fileName.includes(this.value.toLowerCase())) {
|
86
|
+
li.removeAttribute('hidden');
|
87
|
+
} else li.setAttribute('hidden', '');
|
88
|
+
});
|
89
|
+
searchModal.addEventListener('toggle', () => {
|
90
|
+
if (!searchModal.matches(':popover-open')) {
|
91
|
+
document.getElementById('search-input').value = '';
|
92
|
+
}
|
93
|
+
});
|
94
|
+
}
|
95
|
+
});
|
96
|
+
|
97
|
+
document.getElementById('color-scheme').addEventListener('change', function () {
|
98
|
+
applyColorScheme(this.value);
|
99
|
+
Cookies.set('color-scheme', this.value, { expires: 30, path: '', sameSite: 'Strict' })
|
100
|
+
});
|
101
|
+
|
102
|
+
document.addEventListener("DOMContentLoaded", function () {
|
103
|
+
const infoModal = MODALS.querySelector('#info-modal');
|
104
|
+
const VAULTDATA = JSON.parse('{{ site.data.obsidian.vault_data | escape }}'.replace(/"/g, '"'));
|
105
|
+
|
106
|
+
const formattedSize = formatNumberToBytes(VAULTDATA.size);
|
107
|
+
const vaultProps = infoModal.querySelector('#vault-props');
|
108
|
+
vaultProps.textContent =
|
109
|
+
`${VAULTDATA.files} files | ${VAULTDATA.dirs} folders | ${formattedSize}`;
|
110
|
+
|
111
|
+
var colorSchemeCookie = Cookies.get('color-scheme');
|
112
|
+
if (colorSchemeCookie) {
|
113
|
+
document.getElementById('color-scheme').value = applyColorScheme(colorSchemeCookie);
|
114
|
+
} else {
|
115
|
+
console.info('No color-scheme cookie found.');
|
116
|
+
}
|
117
|
+
});
|
118
|
+
|
119
|
+
document.addEventListener('obsidian_modalSelect', function (event) {
|
120
|
+
console.log("modalSelect type: " + event.detail.modalType);
|
121
|
+
switch (event.detail.modalType) {
|
122
|
+
case 'info':
|
123
|
+
|
124
|
+
break;
|
125
|
+
|
126
|
+
case 'search':
|
127
|
+
const searchModal = MODALS.querySelector('#search-modal');
|
128
|
+
const old_ul = searchModal.querySelector('ul');
|
129
|
+
if (old_ul) searchModal.removeChild(old_ul);
|
130
|
+
const ul = document.createElement('ul');
|
131
|
+
searchModal.appendChild(ul);
|
132
|
+
const VAULT_FILES =
|
133
|
+
JSON.parse('{{ site.data.obsidian.vault_files | escape }}'.replace(/"/g, '"'));
|
134
|
+
generateSearchModalFilepaths(VAULT_FILES, ul)
|
135
|
+
break;
|
136
|
+
}
|
137
|
+
});
|
138
|
+
|
139
|
+
document.addEventListener('obsidian_mediaSelect', function (event) {
|
140
|
+
console.log("Selected media: " + event.detail.filePath);
|
141
|
+
while (media.firstChild) media.removeChild(media.firstChild);
|
142
|
+
|
143
|
+
const fullFilePath = getFullFilePath('/' + event.detail.filePath);
|
144
|
+
var supportedType = true;
|
145
|
+
switch (getFileType(fullFilePath)) {
|
146
|
+
default:
|
147
|
+
window.open(fullFilePath, '_blank');
|
148
|
+
supportedType = false;
|
149
|
+
break;
|
150
|
+
case "image":
|
151
|
+
case "svg":
|
152
|
+
const image = document.createElement('img');
|
153
|
+
image.src = fullFilePath;
|
154
|
+
media.appendChild(image);
|
155
|
+
break;
|
156
|
+
case 'audio':
|
157
|
+
const audio = document.createElement('audio');
|
158
|
+
audio.src = fullFilePath;
|
159
|
+
audio.controls = true;
|
160
|
+
media.appendChild(audio);
|
161
|
+
break;
|
162
|
+
case 'video':
|
163
|
+
const video = document.createElement('video');
|
164
|
+
video.src = fullFilePath;
|
165
|
+
video.controls = true;
|
166
|
+
media.appendChild(video);
|
167
|
+
break;
|
168
|
+
}
|
169
|
+
mediaData.innerText = extractFilenameFromPath(fullFilePath);
|
170
|
+
if (supportedType) {
|
171
|
+
mediaModal.addEventListener('wheel', adjustMediaScaleOnScroll);
|
172
|
+
mediaModal.togglePopover();
|
173
|
+
mediaModal.addEventListener('toggle', () => {
|
174
|
+
if (!mediaModal.matches(':popover-open')) {
|
175
|
+
scale = 1;
|
176
|
+
mediaModal.style.scale = scale;
|
177
|
+
mediaModal.removeEventListener('wheel', adjustMediaScaleOnScroll);
|
178
|
+
}
|
179
|
+
});
|
180
|
+
}
|
181
|
+
});
|
182
|
+
</script>
|
183
|
+
|
184
|
+
<script>
|
185
|
+
let scale = 1;
|
186
|
+
function adjustMediaScaleOnScroll(e) {
|
187
|
+
if (e.ctrlKey) {
|
188
|
+
//max scale: 1.6
|
189
|
+
e.preventDefault();
|
190
|
+
console.info("Scroll direction: " + (e.deltaY > 0 ? "down" : "up"));
|
191
|
+
// if (isNaN(scale)) scale = 1;
|
192
|
+
if (e.deltaY < 0) {
|
193
|
+
scale = Math.max(1, scale - 0.01); // Decrease scale
|
194
|
+
} else {
|
195
|
+
scale = Math.min(1.6, scale + 0.01); // Increase scale
|
196
|
+
}
|
197
|
+
mediaModal.style.scale = scale;
|
198
|
+
}
|
199
|
+
}
|
200
|
+
</script>
|
201
|
+
|
202
|
+
<!-- --------------------------- Search modal JS --------------------------- -->
|
203
|
+
<script>
|
204
|
+
function generateSearchModalFilepaths(vaultFiles, ul) {
|
205
|
+
vaultFiles.forEach(child => {
|
206
|
+
if (child.type === 'dir') {
|
207
|
+
generateSearchModalFilepaths(child.children, ul);
|
208
|
+
}
|
209
|
+
if (child.type === 'file') {
|
210
|
+
const li = document.createElement('li');
|
211
|
+
const button = document.createElement('button');
|
212
|
+
const decodedString = he.decode(child.path);
|
213
|
+
// if (decodedString.endsWith('.mdnote')) {
|
214
|
+
// button.textContent = decodedString.slice(0, -4);
|
215
|
+
// } else
|
216
|
+
button.textContent = decodedString;
|
217
|
+
button.addEventListener('click', () => {
|
218
|
+
const searchModal = MODALS.querySelector('#search-modal');
|
219
|
+
dispatchFileSelectEvt(decodedString);
|
220
|
+
searchModal.hidePopover();
|
221
|
+
});
|
222
|
+
li.appendChild(button); ul.appendChild(li);
|
223
|
+
}
|
224
|
+
});
|
225
|
+
}
|
226
|
+
</script>
|
227
|
+
|
228
|
+
<script>
|
229
|
+
async function downloadVaultFiles() {
|
230
|
+
const VAULT_FILES =
|
231
|
+
JSON.parse('{{ site.data.obsidian.vault_files | escape }}'.replace(/"/g, '"'));
|
232
|
+
// Function to recursively collect all file paths
|
233
|
+
const filesToFetch = [];
|
234
|
+
function collectFiles(fileNode) {
|
235
|
+
if (fileNode.type === 'file') {
|
236
|
+
filesToFetch.push(fileNode.path);
|
237
|
+
}
|
238
|
+
if (fileNode.children) {
|
239
|
+
fileNode.children.forEach(collectFiles);
|
240
|
+
}
|
241
|
+
}
|
242
|
+
|
243
|
+
if (Array.isArray(VAULT_FILES)) {
|
244
|
+
VAULT_FILES.forEach(collectFiles);
|
245
|
+
} else {
|
246
|
+
collectFiles(VAULT_FILES);
|
247
|
+
}
|
248
|
+
|
249
|
+
const zip = new JSZip();
|
250
|
+
// Fetch all files and add them to the zip
|
251
|
+
for (const filePath of filesToFetch) {
|
252
|
+
const fullFilePath = he.decode(getFullFilePath(filePath));
|
253
|
+
console.log("fullfilePath vaultDownload " + fullFilePath)
|
254
|
+
const content = await fetch(fullFilePath).then(response => {
|
255
|
+
if (!response.ok) {
|
256
|
+
throw new Error(`Failed to fetch ${fullFilePath}: ${response.statusText}`);
|
257
|
+
}
|
258
|
+
return response.blob();
|
259
|
+
});
|
260
|
+
zip.file(filePath, content, { binary: true });
|
261
|
+
}
|
262
|
+
|
263
|
+
// Generate zip file and trigger download
|
264
|
+
zip.generateAsync({ type: "blob" }).then(function (blob) {
|
265
|
+
// Create a download link
|
266
|
+
const url = URL.createObjectURL(blob);
|
267
|
+
const a = document.createElement('a');
|
268
|
+
a.href = url;
|
269
|
+
a.download = "vaultFiles.zip";
|
270
|
+
document.body.appendChild(a);
|
271
|
+
a.click();
|
272
|
+
document.body.removeChild(a);
|
273
|
+
}).catch(function (error) {
|
274
|
+
console.error('Error while creating the zip:', error);
|
275
|
+
});
|
276
|
+
}
|
277
|
+
</script>
|