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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 450bc32b43ae3caf2e08adc9fda7154419b1fc617014d3e8cb75fe0708591664
4
- data.tar.gz: d388090f953e7e25f4a7232242d46bb6cb115abf9cb739fa8b45494b1f032fef
3
+ metadata.gz: e7d0e540229aa59560164e505b1f9c56bfc06410483f121f0f37de4b35f14648
4
+ data.tar.gz: e134ec448a27e0e1ab0b6c6fb058a71248d9a59c182956a6b873834c150eb120
5
5
  SHA512:
6
- metadata.gz: b3ed73686b9e75eeedd0c4033adaef7f2f7749d0554f08252803f8f908f6173efc869074944e042814760cf9d0150de0d39c92fc56d7bfdec87c250f9e6bb485
7
- data.tar.gz: b147929fea494e53217aa1dfb96957c0aa27d7b5aa4a812e0fe0d1997151d9a8eebfa6788d05871e640c2e6e5085494f221d3372d4304872d1736649dcf106c7
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>
@@ -1,5 +1,5 @@
1
1
 
2
2
  @font-face {
3
3
  font-family: FoulisGreek;
4
- src: url({{ assets/fonts/FoulisGreek.ttf | absolute_path }});
4
+ src: url({{ "assets/fonts/FoulisGreek.ttf" | relative_url }});
5
5
  }
@@ -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
- presentationDoc.title = presentationFrame.title;
117
- await slideshow.start(presentationFrame.contentWindow, config);
118
-
119
- // Ensure that mermaid has already been run!
120
- if (presentationWin.mermaid) {
121
- presentationWin.mermaid.init();
122
- console.log("Ran mermaid!");
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
@@ -178,6 +178,9 @@ main.slideshow-mode {
178
178
  // Transition immediately!
179
179
  transition: width 0s ease, max-width 0s ease;
180
180
  }
181
+
182
+ // Don't scroll, let the inner frame do that.
183
+ overflow: hidden;
181
184
  }
182
185
 
183
186
  // On mobile devices,
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 { font-family: 'Ubuntu Mono', monospace; }
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, '&amp;' ],
6
+ [ /[<]/g, '&lt;' ],
7
+ [ /[>]/g, '&gt;' ],
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('<'), '&lt;');
30
+ assertEq("Test escape multiple <", EscapeHelper.escapeHTML('<a></a>'), '&lt;a&gt;&lt;/a&gt;');
31
+ assertEq("Test escape >", EscapeHelper.escapeHTML('>'), '&gt;');
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;
@@ -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
- prevSlideBtn.disabled = (newSlide.getSlideIndex() == 0);
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 = query.toLowerCase();
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 = query.toLowerCase();
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 context = document.createElement("div");
301
- context.classList.add('context');
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
- link.appendChild(context);
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.11
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-05-28 00:00:00.000000000 Z
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