hematite 0.1.11 → 0.1.14
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/_includes/img/loading_icon.svg +70 -0
- data/_includes/scss/_fonts.scss +1 -1
- data/_layouts/remark_slideshow.html +18 -7
- data/_sass/_layout.scss +3 -0
- data/_sass/_nav.scss +9 -1
- data/assets/html/remark_presentation_frame.html +9 -1
- data/assets/js/EscapeHelper.mjs +37 -0
- data/assets/js/layout/post.mjs +6 -5
- data/assets/js/layout/remark_slideshow.mjs +29 -3
- data/assets/js/search.mjs +36 -6
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7d0e540229aa59560164e505b1f9c56bfc06410483f121f0f37de4b35f14648
|
4
|
+
data.tar.gz: e134ec448a27e0e1ab0b6c6fb058a71248d9a59c182956a6b873834c150eb120
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f0629528aa7d95d26b68b488a2b5110625471856310273f74db557a8b543db35ccb072af00ffed6195d61e7eef1ed50ac755f55b034fe4c6fa41c22c7e0d35df
|
7
|
+
data.tar.gz: 31c8354bfa13a6ce9fb8531835c367214a6419d20ddb5246092271f2caf81fded7e0aba03db2e52e0166b40500d1bb2a4b0f4d31856feb94375c840f095b4f79
|
@@ -0,0 +1,70 @@
|
|
1
|
+
<svg
|
2
|
+
viewBox="0 0 64.0 64.0"
|
3
|
+
version="1.1"
|
4
|
+
class="loading-icon-root"
|
5
|
+
xmlns="http://www.w3.org/2000/svg"
|
6
|
+
xmlns:svg="http://www.w3.org/2000/svg">
|
7
|
+
<style>
|
8
|
+
.loading-icon-root {
|
9
|
+
cursor: pointer;
|
10
|
+
width: 64px;
|
11
|
+
height: 64px;
|
12
|
+
}
|
13
|
+
|
14
|
+
.loading-icon-root .rect {
|
15
|
+
fill: var(--primary-text-color);
|
16
|
+
|
17
|
+
transform: translate(0, 0);
|
18
|
+
transform-origin: 32px 32px;
|
19
|
+
animation: loading-icon-anim 1.5s ease infinite;
|
20
|
+
}
|
21
|
+
|
22
|
+
.loading-icon-root .rect2 {
|
23
|
+
animation-delay: 0.25s;
|
24
|
+
}
|
25
|
+
|
26
|
+
.loading-icon-root .rect3 {
|
27
|
+
animation-delay: 0.5s;
|
28
|
+
}
|
29
|
+
|
30
|
+
.loading-icon-root .rect4 {
|
31
|
+
animation-delay: 0.65s;
|
32
|
+
}
|
33
|
+
|
34
|
+
@keyframes loading-icon-anim {
|
35
|
+
0% { transform: rotate(0); }
|
36
|
+
100% { transform: rotate(-360deg) scale(1, 1); }
|
37
|
+
}
|
38
|
+
</style>
|
39
|
+
<g
|
40
|
+
id="layer1">
|
41
|
+
<rect
|
42
|
+
width="9"
|
43
|
+
height="9"
|
44
|
+
x="10"
|
45
|
+
y="10"
|
46
|
+
ry="4.5"
|
47
|
+
class="rect" />
|
48
|
+
<rect
|
49
|
+
width="9"
|
50
|
+
height="9"
|
51
|
+
x="10"
|
52
|
+
y="10"
|
53
|
+
ry="4.5"
|
54
|
+
class="rect rect2" />
|
55
|
+
<rect
|
56
|
+
width="9"
|
57
|
+
height="9"
|
58
|
+
x="10"
|
59
|
+
y="10"
|
60
|
+
ry="4.5"
|
61
|
+
class="rect rect3" />
|
62
|
+
<rect
|
63
|
+
width="9"
|
64
|
+
height="9"
|
65
|
+
x="10"
|
66
|
+
y="10"
|
67
|
+
ry="4.5"
|
68
|
+
class="rect rect4" />
|
69
|
+
</g>
|
70
|
+
</svg>
|
data/_includes/scss/_fonts.scss
CHANGED
@@ -14,6 +14,12 @@ layout: default
|
|
14
14
|
|
15
15
|
{% assign frame_resource_url = 'assets/html/remark_presentation_frame.html' | relative_url %}
|
16
16
|
|
17
|
+
<div class='loading-icon-container'>
|
18
|
+
<center>
|
19
|
+
{% include img/loading_icon.svg %}
|
20
|
+
</center>
|
21
|
+
</div>
|
22
|
+
|
17
23
|
<main class="slideshow-mode">
|
18
24
|
<iframe
|
19
25
|
id="presentation_frame"
|
@@ -113,13 +119,18 @@ layout: default
|
|
113
119
|
`);
|
114
120
|
|
115
121
|
presentationWin.initPresentation = (async () => {
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
presentationWin.mermaid
|
122
|
-
|
122
|
+
try {
|
123
|
+
presentationDoc.title = presentationFrame.title;
|
124
|
+
await slideshow.start(presentationFrame.contentWindow, config);
|
125
|
+
|
126
|
+
// Ensure that mermaid has already been run!
|
127
|
+
if (presentationWin.mermaid) {
|
128
|
+
presentationWin.mermaid.init();
|
129
|
+
}
|
130
|
+
}
|
131
|
+
finally {
|
132
|
+
// Hide the loading icon
|
133
|
+
document.querySelector(".loading-icon-container")?.remove();
|
123
134
|
}
|
124
135
|
});
|
125
136
|
|
data/_sass/_layout.scss
CHANGED
data/_sass/_nav.scss
CHANGED
@@ -124,7 +124,6 @@ nav.sidebar {
|
|
124
124
|
transform: scale(0, 1) translate(-30px, 0);
|
125
125
|
|
126
126
|
color: var(--primary-text-color);
|
127
|
-
background-color: var(--primary-background-color);
|
128
127
|
box-shadow: 0 0 2px var(--shadow-color-light);
|
129
128
|
padding: $navbar-padding;
|
130
129
|
|
@@ -138,6 +137,15 @@ nav.sidebar {
|
|
138
137
|
transform-origin: left;
|
139
138
|
transition: opacity 0.5s ease, transform 0.5s ease, width 0.5s ease;
|
140
139
|
|
140
|
+
// Work around a rendering bug in Safari — apply the background color to both
|
141
|
+
// the results pane and the container.
|
142
|
+
//
|
143
|
+
// Without this, Safari may display elements of pinned-pages in the same
|
144
|
+
// location as the search results.
|
145
|
+
&, .search-results {
|
146
|
+
background-color: var(--primary-background-color);
|
147
|
+
}
|
148
|
+
|
141
149
|
|
142
150
|
// Container with search box and button
|
143
151
|
> .search-container {
|
@@ -20,7 +20,15 @@ h1, h2, h3 {
|
|
20
20
|
font-weight: normal;
|
21
21
|
}
|
22
22
|
|
23
|
-
.remark-code, .remark-inline-code
|
23
|
+
.remark-code, .remark-inline-code, body.mdSourceView {
|
24
|
+
font-family: 'Ubuntu Mono', monospace;
|
25
|
+
}
|
26
|
+
|
27
|
+
body.mdSourceView {
|
28
|
+
padding-top: 50px;
|
29
|
+
background: white;
|
30
|
+
color: black;
|
31
|
+
}
|
24
32
|
|
25
33
|
/* Show hidden slides to allow preprocessors (e.g. mermaid) to
|
26
34
|
properly account for container size.
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
import { assertEq } from "./assertions.mjs";
|
3
|
+
|
4
|
+
let htmlReplacements = [
|
5
|
+
[ /[&]/g, '&' ],
|
6
|
+
[ /[<]/g, '<' ],
|
7
|
+
[ /[>]/g, '>' ],
|
8
|
+
];
|
9
|
+
|
10
|
+
var EscapeHelper = {
|
11
|
+
escapeHTML(text) {
|
12
|
+
for (const item of htmlReplacements) {
|
13
|
+
text = text.replace(item[0], item[1]);
|
14
|
+
}
|
15
|
+
|
16
|
+
return text;
|
17
|
+
},
|
18
|
+
// Escape for usage within a regex expression
|
19
|
+
escapeRegex(text) {
|
20
|
+
return text.replace(/([\*\(\).\[\]\{\}\^\$\-\=\;\:\'\"\\])/g, '\\$1'); //'
|
21
|
+
},
|
22
|
+
// Escape text for usage within a replacement pattern
|
23
|
+
// E.g. "foobar".replaceAll("foo", text);
|
24
|
+
escapeReplacePattern(text) {
|
25
|
+
return text.replace(/\$/g, '$$$$');
|
26
|
+
}
|
27
|
+
};
|
28
|
+
|
29
|
+
assertEq("Test escape <", EscapeHelper.escapeHTML('<'), '<');
|
30
|
+
assertEq("Test escape multiple <", EscapeHelper.escapeHTML('<a></a>'), '<a></a>');
|
31
|
+
assertEq("Test escape >", EscapeHelper.escapeHTML('>'), '>');
|
32
|
+
assertEq("Test escape identity", EscapeHelper.escapeHTML('Hello, world.'), 'Hello, world.');
|
33
|
+
assertEq("Test regex escape simple", EscapeHelper.escapeRegex(".*"), "\\.\\*");
|
34
|
+
assertEq("Test regex more complicated escape", EscapeHelper.escapeRegex("This, is a test... :)"), "This, is a test\\.\\.\\. \\:\\)");
|
35
|
+
assertEq("Test replace pattern escape", EscapeHelper.escapeReplacePattern("0$ and 24 cents"), "0$$ and 24 cents");
|
36
|
+
|
37
|
+
export default EscapeHelper;
|
data/assets/js/layout/post.mjs
CHANGED
@@ -37,6 +37,12 @@ function createTagLinks(page) {
|
|
37
37
|
let label = document.querySelector("#post_tags > #tag_header_lbl");
|
38
38
|
label.innerText = stringLookup(`tags`);
|
39
39
|
|
40
|
+
// Don't show the tag container if there aren't any tags
|
41
|
+
if (!page.tags || page.tags.length == 0) {
|
42
|
+
container.style.display = "none";
|
43
|
+
return;
|
44
|
+
}
|
45
|
+
|
40
46
|
for (const tag of page.tags) {
|
41
47
|
let tagElem = document.createElement("a");
|
42
48
|
tagElem.style.display = "inline-block";
|
@@ -47,11 +53,6 @@ function createTagLinks(page) {
|
|
47
53
|
|
48
54
|
container.appendChild(tagElem);
|
49
55
|
}
|
50
|
-
|
51
|
-
// Don't show the tag container if there aren't any tags
|
52
|
-
if (page.tags.length == 0) {
|
53
|
-
container.style.display = "none";
|
54
|
-
}
|
55
56
|
}
|
56
57
|
|
57
58
|
function fillDate(page) {
|
@@ -77,6 +77,20 @@ function focusSlideFromHash(slideshow) {
|
|
77
77
|
}
|
78
78
|
}
|
79
79
|
|
80
|
+
/// Returns true iff the slide viewer should only display markdown.
|
81
|
+
function shouldOnlyDisplayMd() {
|
82
|
+
let pageArgs = UrlHelper.getPageArgs();
|
83
|
+
if (!pageArgs) {
|
84
|
+
return false;
|
85
|
+
}
|
86
|
+
|
87
|
+
if (pageArgs.md_only) {
|
88
|
+
return true;
|
89
|
+
}
|
90
|
+
|
91
|
+
return false;
|
92
|
+
}
|
93
|
+
|
80
94
|
async function main(targetWindow, config) {
|
81
95
|
// True if touch navigation is enabled.
|
82
96
|
let usingCustomTouchNav = false;
|
@@ -88,6 +102,13 @@ async function main(targetWindow, config) {
|
|
88
102
|
}));
|
89
103
|
}
|
90
104
|
|
105
|
+
// If the user has requested that only the page's markdown be shown,
|
106
|
+
if (shouldOnlyDisplayMd()) {
|
107
|
+
targetWindow.document.body.innerText = config?.source;
|
108
|
+
targetWindow.document.body.classList.add('mdSourceView');
|
109
|
+
return;
|
110
|
+
}
|
111
|
+
|
91
112
|
// Customize touchscreen navigation — the default remark
|
92
113
|
// navigation can break buttons, zooming.
|
93
114
|
if (config?.navigation?.touch === true
|
@@ -150,6 +171,7 @@ async function main(targetWindow, config) {
|
|
150
171
|
|
151
172
|
if (handlingGesture) {
|
152
173
|
evt.preventDefault();
|
174
|
+
elemContainer.setPointerCapture();
|
153
175
|
}
|
154
176
|
}
|
155
177
|
});
|
@@ -205,16 +227,20 @@ function addExtendedControls(targetWindow, slideshow) {
|
|
205
227
|
targetWindow.print();
|
206
228
|
};
|
207
229
|
|
230
|
+
let updateBtns = (slideIdx) => {
|
231
|
+
prevSlideBtn.disabled = (slideIdx == 0);
|
232
|
+
nextSlideBtn.disabled = (slideIdx + 1 >= slideshow.getSlideCount());
|
233
|
+
};
|
234
|
+
|
208
235
|
slideshow.on('showSlide', function(newSlide) {
|
209
236
|
if (!newSlide) {
|
210
237
|
return;
|
211
238
|
}
|
212
239
|
|
213
|
-
|
214
|
-
nextSlideBtn.disabled = (newSlide.getSlideIndex() + 1 >= slideshow.getSlideCount());
|
240
|
+
updateBtns(newSlide.getSlideIndex());
|
215
241
|
});
|
216
242
|
|
217
|
-
|
243
|
+
updateBtns(0);
|
218
244
|
|
219
245
|
nav.replaceChildren(prevSlideBtn, nextSlideBtn, spacer, printBtn);
|
220
246
|
targetWindow.document.body.appendChild(nav);
|
data/assets/js/search.mjs
CHANGED
@@ -7,6 +7,7 @@ import { expandContainingDropdowns } from "./dropdownExpander.mjs";
|
|
7
7
|
import AnimationUtil from "./AnimationUtil.mjs";
|
8
8
|
import AsyncUtil from "./AsyncUtil.mjs";
|
9
9
|
import UrlHelper from "./UrlHelper.mjs";
|
10
|
+
import EscapeHelper from "./EscapeHelper.mjs";
|
10
11
|
|
11
12
|
const PAGE_DATA_URL = `{{ "/assets/search_data.json" | relative_url }}`;
|
12
13
|
const MATCHING_TITLE_PRIORITY_INCREMENT = 15;
|
@@ -50,15 +51,25 @@ class Searcher {
|
|
50
51
|
.replaceAll(/[&]ldquo;/g, '"')
|
51
52
|
.replaceAll(/[&]rdquo;/g, '"')
|
52
53
|
.replaceAll(/[&]amp;/g, "&")
|
54
|
+
// Remove the astrisks around **bolded** text
|
55
|
+
.replaceAll(/(\s|^)[*]{2}[^*]+[*]{2}(\s|$)/g, "$1$2$3")
|
56
|
+
// Remove the backticks around `text` that is code-formatted
|
57
|
+
.replaceAll(/(\s|^)[`]([^`]+)[`](\s|$)/g, "$1$2$3") // `
|
53
58
|
.replaceAll(/\s+/g, ' ');
|
54
59
|
|
55
60
|
}
|
56
61
|
|
62
|
+
filterQuery_(query) {
|
63
|
+
return query
|
64
|
+
.toLowerCase()
|
65
|
+
.replaceAll(/\s+/g, ' ');
|
66
|
+
}
|
67
|
+
|
57
68
|
/// Get number of full matches for [query] in [text].
|
58
69
|
/// @precondition [text] is already in a searchable (i.e.
|
59
70
|
/// filtered) form.
|
60
71
|
getNumberOfMatches(query, text) {
|
61
|
-
query =
|
72
|
+
query = this.filterQuery_(query);
|
62
73
|
|
63
74
|
return text.toLowerCase().split(query).length - 1;
|
64
75
|
}
|
@@ -68,7 +79,7 @@ class Searcher {
|
|
68
79
|
/// is found.
|
69
80
|
/// @precondition [text] is already in a searchable form.
|
70
81
|
getIdxOfFirstMatch(query, text, startPos) {
|
71
|
-
query =
|
82
|
+
query = this.filterQuery_(query);
|
72
83
|
|
73
84
|
return text.toLowerCase().indexOf(query, startPos);
|
74
85
|
}
|
@@ -167,6 +178,7 @@ class Searcher {
|
|
167
178
|
results.push({
|
168
179
|
index,
|
169
180
|
context,
|
181
|
+
matchIndex: matchLoc,
|
170
182
|
pageData,
|
171
183
|
});
|
172
184
|
|
@@ -295,17 +307,35 @@ function handleSearch(searcher) {
|
|
295
307
|
|
296
308
|
searchResults.replaceChildren(descriptionElem);
|
297
309
|
|
310
|
+
// Adds HTML to bold/italicize all results in [context].
|
311
|
+
// [context] is HTML-escaped before adding to the result.
|
312
|
+
const boldResults = (context) => {
|
313
|
+
let result = "";
|
314
|
+
let queryRegex = new RegExp(`(${EscapeHelper.escapeRegex(query)})`, `ig`);
|
315
|
+
let contextHTML = "";
|
316
|
+
let lastIdx = 0;
|
317
|
+
for (const match of context.matchAll(queryRegex)) {
|
318
|
+
result += EscapeHelper.escapeHTML(context.substring(lastIdx, match.index));
|
319
|
+
result += "<b><i>" + EscapeHelper.escapeHTML(match[0]) + "</i></b>";
|
320
|
+
lastIdx = match.index + match[0].length;
|
321
|
+
}
|
322
|
+
result += EscapeHelper.escapeHTML(context.substring(lastIdx));
|
323
|
+
|
324
|
+
return result;
|
325
|
+
};
|
326
|
+
|
298
327
|
for (const result of results) {
|
299
328
|
let link = document.createElement("a");
|
300
|
-
let
|
301
|
-
|
329
|
+
let contextElem = document.createElement("div");
|
330
|
+
contextElem.classList.add('context');
|
302
331
|
|
303
332
|
link.innerText = result.pageData.title ?? stringLookup(`untitled`);
|
304
333
|
link.href =
|
305
334
|
result.pageData.url + `?query=${escape(query)},index=${result.index}`;
|
306
|
-
context.innerText = result.context;
|
307
335
|
|
308
|
-
|
336
|
+
contextElem.innerHTML = boldResults(result.context);
|
337
|
+
|
338
|
+
link.appendChild(contextElem);
|
309
339
|
searchResults.appendChild(link);
|
310
340
|
}
|
311
341
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hematite
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Henry Heino
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-06-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jekyll
|
@@ -37,6 +37,7 @@ files:
|
|
37
37
|
- _includes/extern_library_imports.html
|
38
38
|
- _includes/footer.html
|
39
39
|
- _includes/img/hamburger_menu.svg
|
40
|
+
- _includes/img/loading_icon.svg
|
40
41
|
- _includes/img/search_icon.svg
|
41
42
|
- _includes/katex_includes.html
|
42
43
|
- _includes/mermaid_includes.html
|
@@ -74,6 +75,7 @@ files:
|
|
74
75
|
- assets/js/AnimationUtil.mjs
|
75
76
|
- assets/js/AsyncUtil.mjs
|
76
77
|
- assets/js/DateUtil.mjs
|
78
|
+
- assets/js/EscapeHelper.mjs
|
77
79
|
- assets/js/PageAlert.mjs
|
78
80
|
- assets/js/Settings.mjs
|
79
81
|
- assets/js/UrlHelper.mjs
|