hematite 0.0.12 → 0.1.4

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: 8ba3735a0bf64aa9c895fc9622e0370b0827c80386469ccb4c5172058f91db97
4
- data.tar.gz: e6f824c10b0febde44d427ee0528681f756cc59bff67a3362e256d77d5be3650
3
+ metadata.gz: 57c0b3ae9bdec4a2e37d649a52dfbacb84a8c09b258d2a5bb676571c5fed060d
4
+ data.tar.gz: '09bc9aa4956973f5cbb3dfa1e4889b9adfe70928eb5f2f25518b09b863a4e8ed'
5
5
  SHA512:
6
- metadata.gz: 958067abc2e83cbecc509dc7c31c2eb711c2116ea201a27c28f6a89bc0f0a6658d34532433596b989178ab070d686c2a5de0d26781ff6b92d73fdc6416f9608f
7
- data.tar.gz: e01cb8c28899e24135e832ef89543c9266e66db13d9a53f40327d6c32b6d8ed815d5da7247a63fd88ca94dc762180ab752fb8494c2d9e31d3ae2ed13fdda6297
6
+ metadata.gz: 86065a34d00cc7d8844bdd02883dc7c8dd25c4c6436a1d4b28747d0140d4409d0ac7e7a910d8fe4f54ba546c303208cb347d598141a6e66b1d0c5607e282f249
7
+ data.tar.gz: 2b218f88fec888c6029650fdd29b0809729cd321905e25e9151e26173347e7488813eebcc79d32c7ced657d8a4ce7a0e2acb4b9386e9d683607b6622e6911784
@@ -0,0 +1,11 @@
1
+ <!--
2
+ Include all external libraries (that are bundled with Hematite) requested by the
3
+ page
4
+ -->
5
+
6
+ {% include mermaid_includes.html %}
7
+ {% include katex_includes.html %}
8
+
9
+ <!-- Page/site-specific imports and configuration -->
10
+ {{ site.additional_import_html | default: "" }}
11
+ {{ page.additional_import_html | default: "" }}
@@ -4,7 +4,7 @@
4
4
  KaTeX can be enabled with page.katex.
5
5
  -->
6
6
 
7
- {% if page.katex %}
7
+ {% if page.katex || site.mermaid %}
8
8
 
9
9
  <!-- See the releases tab on https://github.com/KaTeX/KaTeX -->
10
10
  <link
@@ -0,0 +1,24 @@
1
+
2
+ <!--
3
+ Include mermaid-js scripts.
4
+ -->
5
+ {% if page.mermaid || site.mermaid %}
6
+ <script>
7
+ var MERMAID_CONFIG = window.MERMAID_CONFIG ?? {{ site.mermaid_config | jsonify }} ?? { };
8
+
9
+ // Load defaults
10
+ MERMAID_CONFIG.startOnLoad ??= true;
11
+ MERMAID_CONFIG.htmlLabels ??= true;
12
+ </script>
13
+ <script
14
+ defer
15
+ src="{{ "assets/plugin/mermaid/mermaid.js" | relative_url }}"
16
+ {% comment %}
17
+ Here, we're also calling "mermaid.init()" because of the defer attribute — it's
18
+ possible that the window has already loaded (and thus startOnLoad may have no effect).
19
+ {% endcomment %}
20
+ onload="mermaid.initialize(MERMAID_CONFIG); mermaid.init();" >
21
+ </script>
22
+
23
+ {% endif %}
24
+
@@ -10,10 +10,14 @@ title: Untitled
10
10
  {% assign content_start = page.content | slice: 0, 100 %}
11
11
  <meta name="description" content={{ page.description | default: content_start | strip_html | jsonify }}/>
12
12
  <link rel="stylesheet" href="{{ "assets/style.css" | relative_url }}"/>
13
- <link rel="icon" href="{{ page.favicon_path | default: site.favicon_path | default: "assets/img/favicon.svg" | absolute_url }}"/>
13
+ {% if page.favicon_url_absolute or site.favicon_url_absolute %}
14
+ <link rel="icon" href="{{ page.favicon_url_absolute | default: site.favicon_url_absolute }}"/>
15
+ {% else %}
16
+ <link rel="icon" href="{{ page.favicon_url | default: site.favicon_url | default: "assets/img/favicon.svg" | absolute_url }}"/>
17
+ {% endif %}
14
18
  <script type="module" src="{{ "assets/js/main.mjs" | relative_url }}"></script>
15
19
 
16
- {% include katex_includes.html %}
20
+ {% include extern_library_imports.html %}
17
21
 
18
22
  <title>{{ page.title }} — {{ site.title }}</title>
19
23
  </head>
@@ -0,0 +1,88 @@
1
+ ---
2
+ layout: default
3
+ ---
4
+
5
+ <!-- Defines a https://remarkjs.com/ slideshow -->
6
+ <!-- Configure remark with
7
+ page.remark_presentation_config, site.remark_presentation_config,
8
+ page.remark_presentation_config_html
9
+ -->
10
+
11
+ {% assign frame_resource_url = 'assets/html/remark_presentation_frame.html.resource' | relative_url %}
12
+
13
+ <main class="slideshow-mode">
14
+ <iframe
15
+ id="presentation_frame"
16
+ onload="window.presentationFrameLoaded = true;"></iframe>
17
+ </main>
18
+
19
+ <script defer type="module">
20
+ import slideshow from "{{ 'assets/js/layout/remark_slideshow.mjs' | relative_url }}";
21
+
22
+ let presentationFrame = document.querySelector("#presentation_frame");
23
+ presentationFrame.src = {{ frame_resource_url | jsonify }};
24
+ window.presentationFrameLoaded = false;
25
+
26
+ let config =
27
+ {{ page.remark_presentation_config | default: site.remark_presentation_config | jsonify }};
28
+ config ??= {};
29
+
30
+ config.source = {{ page.content | jsonify }};
31
+
32
+ // Library imports intended for this page: Forward to the frame.
33
+ let libraryImports = unescape(`
34
+ {% capture library_imports %}
35
+ {% include extern_library_imports.html %}
36
+ {% endcapture %}
37
+ {{ library_imports | url_encode | replace: "+", " " }}
38
+ `);
39
+
40
+ // To be called after the presentation iframe loads.
41
+ window.initSlides = () => {
42
+ let presentationDoc = presentationFrame.contentDocument;
43
+ let presentationWin = presentationFrame.contentWindow;
44
+
45
+ // Ensure that onload doesn't get called again if the frame reloads.
46
+ presentationFrame.onload = null;
47
+
48
+ // Re-create the document, add the library imports to it.
49
+ let slideshowHtml = presentationDoc.documentElement.outerHTML;
50
+
51
+ presentationDoc.open();
52
+ presentationDoc.write("<!DOCTYPE html>");
53
+ presentationDoc.write(slideshowHtml);
54
+ presentationDoc.write(libraryImports);
55
+
56
+ // Any additional configuration scripts/html specific to remark
57
+ presentationDoc.write(unescape(
58
+ {{ page.remark_presentation_config_html | default: "" | url_encode | replace: "+", " " | jsonify }}
59
+ ));
60
+
61
+ presentationDoc.write(`
62
+ <${"script"}>
63
+ if (window.MERMAID_CONFIG) {
64
+ window.MERMAID_CONFIG.useMaxWidth = true;
65
+ window.MERMAID_CONFIG.cloneCssStyles = false;
66
+ window.MERMAID_CONFIG.flowchart = { useMaxWidth: true };
67
+ }
68
+ </${"script"}>
69
+ `);
70
+ presentationDoc.close();
71
+ (async () => {
72
+ await slideshow.start(presentationFrame.contentWindow, config);
73
+
74
+ // Ensure that mermaid has already been run!
75
+ if (presentationWin.mermaid) {
76
+ presentationWin.mermaid.init();
77
+ console.log("Ran mermaid!");
78
+ }
79
+ })();
80
+ };
81
+
82
+ if (window.presentationFrameLoaded) {
83
+ initSlides();
84
+ }
85
+ else {
86
+ presentationFrame.onload = initSlides;
87
+ }
88
+ </script>
data/_sass/_elements.scss CHANGED
@@ -117,14 +117,31 @@ input::placeholder {
117
117
  }
118
118
  }
119
119
 
120
+ table {
121
+ border-collapse: collapse;
122
+ }
123
+
120
124
  table td {
121
125
  border: 1px solid var(--line-color-light);
126
+ padding: 3px;
127
+ }
128
+
129
+ table th {
130
+ padding: 4px;
131
+ font-weight: bold;
122
132
  }
123
133
 
124
134
  fieldset {
125
135
  border-color: var(--line-color-normal);
126
136
  margin-bottom: 30px;
127
137
  }
138
+
139
+ blockquote {
140
+ border-left: 1px solid var(--line-color-light);
141
+ margin-left: 10px;
142
+ padding-left: 20px;
143
+ color: var(--text-color-faint);
144
+ }
128
145
  }
129
146
 
130
147
  // Markdown parsers may put `code` elements inside of `pre`
data/_sass/_hljs.scss ADDED
@@ -0,0 +1,36 @@
1
+
2
+ // Created while referencing rouge-github.scss as published
3
+ // at https://github.com/pages-themes/cayman/blob/master/_sass/rouge-github.scss
4
+
5
+ .hljs {
6
+ color: var(--syntax-normal-fg);
7
+ display: block;
8
+ white-space: pre-wrap;
9
+
10
+ // Keywords and literals
11
+ .hljs-keyword, .hljs-literal {
12
+ color: var(--syntax-keyword-fg);
13
+ }
14
+
15
+ .hljs-literal {
16
+ font-weight: bold;
17
+ color: var(--syntax-name-fg);
18
+ }
19
+
20
+ // Strings
21
+ .hljs-string {
22
+ color: var(--syntax-string-fg);
23
+ }
24
+
25
+ // Numbers
26
+ .hljs-number {
27
+ color: var(--syntax-number-fg);
28
+ }
29
+
30
+
31
+ // Comments
32
+ .hljs-comment {
33
+ color: var(--syntax-comment-fg);
34
+ font-style: italic;
35
+ }
36
+ }
data/_sass/_layout.scss CHANGED
@@ -154,6 +154,21 @@ nav#post_next_prev {
154
154
  flex-grow: 1;
155
155
  }
156
156
 
157
+ main.slideshow-mode {
158
+ position: absolute;
159
+ display: flex;
160
+
161
+ left: 0;
162
+ right: 0;
163
+ top: var(--header-effective-height);
164
+ bottom: 0;
165
+
166
+ iframe {
167
+ flex-grow: 1;
168
+ border: none;
169
+ }
170
+ }
171
+
157
172
  // On mobile devices,
158
173
  @media screen and (max-width: $site-content-preferred-width) {
159
174
  .main-container {
data/_sass/_nav.scss CHANGED
@@ -41,6 +41,14 @@ body > header {
41
41
  }
42
42
  }
43
43
 
44
+ :root {
45
+ --header-effective-height: var(--header-height);
46
+ }
47
+
48
+ :root.minimizedNavHeader {
49
+ --header-effective-height: 0px;
50
+ }
51
+
44
52
  :root.minimizedNavHeader body > header {
45
53
  > *:not(#toggle_sidebar_btn) {
46
54
  opacity: 0;
@@ -48,6 +56,11 @@ body > header {
48
56
  transition: opacity 0.5s ease;
49
57
  }
50
58
 
59
+ #toggle_sidebar_btn {
60
+ padding-left: 4px;
61
+ width: var(--header-height);
62
+ }
63
+
51
64
  word-wrap: break-word;
52
65
  width: var(--header-height);
53
66
  padding: 0;
data/_sass/hematite.scss CHANGED
@@ -12,3 +12,4 @@
12
12
  @import "_layout";
13
13
  @import "_elements";
14
14
  @import "_rogue";
15
+ @import "_hljs";
@@ -0,0 +1,69 @@
1
+ ---
2
+ ---
3
+ <!-- Using a .html.resource file to prevent Jekyll from changing the extension -->
4
+
5
+ <!DOCTYPE html>
6
+ <html class="lightTheme" lang="{{ page.lang | default: site.lang | default: "en-US" }}">
7
+ <head>
8
+ <meta name="viewport" content="width=device-width,initial-scale=1.0"/>
9
+ <meta charset="utf-8"/>
10
+ <!-- Content of remark presentation iframe. Based on https://github.com/gnab/remark/wiki#getting-started -->
11
+ <style>
12
+ /* Stylesheet mostly taken from https://github.com/gnab/remark/wiki#getting-started */
13
+ @import url(https://fonts.googleapis.com/css?family=Yanone+Kaffeesatz);
14
+ @import url(https://fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic);
15
+ @import url(https://fonts.googleapis.com/css?family=Ubuntu+Mono:400,700,400italic);
16
+
17
+ body { font-family: 'Droid Serif'; }
18
+ h1, h2, h3 {
19
+ font-family: 'Yanone Kaffeesatz';
20
+ font-weight: normal;
21
+ }
22
+
23
+ .remark-code, .remark-inline-code { font-family: 'Ubuntu Mono'; }
24
+
25
+ /* Show hidden slides to allow preprocessors (e.g. mermaid) to
26
+ properly account for container size.
27
+ */
28
+ @media screen {
29
+ .remark-slide-container:not(.remark-visible) {
30
+ display: block;
31
+ box-shadow: none;
32
+ z-index: -999;
33
+ visibility: hidden;
34
+ }
35
+ }
36
+
37
+ nav {
38
+ display: flex;
39
+
40
+ position: absolute;
41
+ z-index: 10000;
42
+ bottom: 0;
43
+ left: 0;
44
+ right: 0;
45
+ opacity: 0.8;
46
+ }
47
+
48
+ nav:hover, nav:focus-within {
49
+ opacity: 1;
50
+ }
51
+
52
+ @media print {
53
+ nav {
54
+ display: none;
55
+ }
56
+
57
+ /* Prevent parts of slides from being cut off on Firefox */
58
+ .remark-slide-scaler {
59
+ transform: none !important;
60
+ }
61
+ }
62
+
63
+ </style>
64
+ <link rel="stylesheet" href="{{ 'assets/style_only_syntax.css' | relative_url }}" />
65
+ </head>
66
+ <body>
67
+ </body>
68
+ <script src="{{ 'assets/plugin/remark_presenter/remark.min.js' | relative_url }}"></script>
69
+ </html>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg width="512" height="512" version="1.1" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
3
+ <defs>
4
+ <filter id="filter7674" x="-.25" y="-.25" width="1.5" height="1.5" color-interpolation-filters="sRGB">
5
+ <feGaussianBlur stdDeviation="12.0308382"/>
6
+ </filter>
7
+ </defs>
8
+ <g>
9
+ <path d="m122.52 34.551-94.311 117.48 4.503 246.11 127.26 70.081 258.18-41.737 71.954-66.215-38.507-266.18s-1.2406-30.237-85.384-44.738-243.69-14.797-243.69-14.797z" fill="#9300cf" filter="url(#filter7674)" transform="scale(1.01) rotate(30deg) translate(2, 10)" transform-origin="center"/>
10
+ <path d="m122.52 34.551-94.311 117.48 4.503 246.11 127.26 70.081 258.18-41.737 71.954-66.215-38.507-266.18s-1.2406-30.237-85.384-44.738-243.69-14.797-243.69-14.797z" fill="#f61616"/>
11
+ </g>
12
+ </svg>
@@ -62,6 +62,22 @@ var UrlHelper = {
62
62
  return url.substring(hashLoc, argsStart);
63
63
  },
64
64
 
65
+ // Replace an existing hash with a [newHash]. The new hash
66
+ //
67
+ withReplacedHash(url, newHash) {
68
+ let hashLoc = url.lastIndexOf('#');
69
+
70
+ if ((newHash + "").charAt(0) != '#') {
71
+ newHash = `#${newHash}`;
72
+ }
73
+
74
+ if (hashLoc == -1) {
75
+ return url + newHash;
76
+ }
77
+
78
+ return url.substring(0, hashLoc) + newHash;
79
+ },
80
+
65
81
  /// Remove metadata encoded in the given URL and returns
66
82
  /// it.
67
83
  /// If [url] is undefined, uses the page's URL.
@@ -115,4 +131,8 @@ assertEq("Trimming metadata",
115
131
  "https://example.com/"
116
132
  );
117
133
 
134
+ assertEq("Replacing a hash",
135
+ UrlHelper.withReplacedHash("https://example.com/#foo", "bar"),
136
+ "https://example.com/#bar");
137
+
118
138
  export default UrlHelper;
@@ -51,7 +51,8 @@ function expandBasedOnURL() {
51
51
  const doExpansion = (url) => {
52
52
  // Determine the hash.
53
53
  let hash = UrlHelper.getPageHash();
54
- if (hash == null) {
54
+ let isInvalid = hash && /^\#\d+/.exec(hash);
55
+ if (hash == null || isInvalid) {
55
56
  return;
56
57
  }
57
58
 
@@ -0,0 +1,164 @@
1
+ import { getUrlQuery, Searcher } from "../search.mjs";
2
+ import { stringLookup } from "../strings.mjs";
3
+ import UrlHelper from "../UrlHelper.mjs";
4
+
5
+ function isInPresenterMode(targetDoc) {
6
+ return targetDoc.body.classList.contains("remark-presenter-mode");
7
+ }
8
+
9
+ function focusSearchResult(targetWin, query, targetMatchNo, slideshow, searcher) {
10
+ let matchesFound = 0;
11
+ for (const slide of slideshow.getSlides()) {
12
+ let targetText = (slide.content ?? []).join("\n");
13
+ let currentSlideNo = slide.getSlideIndex() + 1;
14
+
15
+ matchesFound += searcher.getNumberOfMatches(query, targetText);
16
+ console.log("Considering ", targetText, " with ", matchesFound, "matches found so far");
17
+
18
+ if (matchesFound > targetMatchNo) {
19
+ slideshow.gotoSlide(currentSlideNo);
20
+ return;
21
+ }
22
+
23
+ // Now search the notes!
24
+ if (slide.notes) {
25
+ targetText = slide.notes.join("\n");
26
+ matchesFound += searcher.getNumberOfMatches(query, targetText);
27
+ if (matchesFound > targetMatchNo) {
28
+ slideshow.gotoSlide(currentSlideNo);
29
+
30
+ if (!isInPresenterMode(targetWin.document)) {
31
+ slideshow.togglePresenterMode();
32
+ }
33
+ return;
34
+ }
35
+ }
36
+ }
37
+
38
+ console.log("Found ", matchesFound, " matches, which is less than the target of ", targetMatchNo);
39
+ slideshow.gotoLastSlide();
40
+ return -1;
41
+ }
42
+
43
+ /// Show a search result the user requested through the
44
+ /// page's URL.
45
+ function focusSearchResultFromUrl(targetWin, slideshow) {
46
+ let { query, resultIndex } = getUrlQuery() ?? {};
47
+ let index = resultIndex;
48
+
49
+ if (query === undefined || index === undefined) {
50
+ return;
51
+ }
52
+
53
+ console.log("Focusing a search result: ", query, index);
54
+
55
+ let searcher = new Searcher();
56
+ focusSearchResult(targetWin, query, index, slideshow, searcher);
57
+ }
58
+
59
+ function focusSlideFromHash(slideshow) {
60
+ let hash = UrlHelper.getPageHash();
61
+ if (!hash) {
62
+ return;
63
+ }
64
+
65
+ let targetSlide = parseInt(hash.substring(1));
66
+ if (targetSlide) {
67
+ console.log("Navigating to slide", targetSlide);
68
+ slideshow.gotoSlide(targetSlide);
69
+ }
70
+ }
71
+
72
+ async function main(targetWindow, config) {
73
+ if (!targetWindow.remark) {
74
+ // Wait for page load if remark isn't available yet.
75
+ await (new Promise(resolve => {
76
+ targetWindow.addEventListener('load', resolve);
77
+ }));
78
+ }
79
+
80
+ // See https://remarkjs.com/#8
81
+ let slideshow = targetWindow.remark.create(config);
82
+
83
+ // For debugging
84
+ window.slideshow_debug = slideshow;
85
+
86
+ targetWindow.focus();
87
+
88
+ addExtendedControls(targetWindow, slideshow);
89
+ focusSearchResultFromUrl(targetWindow, slideshow);
90
+
91
+ targetWindow.history.replaceState(null, targetWindow.location.href);
92
+ let targetWinHistory = targetWindow.history.state;
93
+
94
+ focusSlideFromHash(slideshow);
95
+
96
+ window.addEventListener('hashchange', () => {
97
+ focusSlideFromHash(slideshow);
98
+ });
99
+
100
+ slideshow.on('showSlide', function(newSlide) {
101
+ if (!newSlide) {
102
+ return;
103
+ }
104
+ let hashId = newSlide.getSlideIndex() + 1;
105
+
106
+ // Update the window's URL to match that of the interior
107
+ // (e.g. slide 3 = #3).
108
+ // Try not to have the forward/back arrows go forward/back in the iframe's history.
109
+ targetWindow.history.replaceState(
110
+ targetWinHistory,
111
+ UrlHelper.withReplacedHash(targetWindow.location.href, hashId));
112
+ window.location.hash = hashId;
113
+ });
114
+ }
115
+
116
+ /// Apply minor adjustments to the default remark layout
117
+ function addExtendedControls(targetWindow, slideshow) {
118
+ let slideContainer = targetWindow.document.querySelector(".remark-slides-area");
119
+
120
+ // Announce changes to the slide (e.g. going to the next slide).
121
+ slideContainer.setAttribute("aria-live", "polite");
122
+
123
+ // Add next/previous buttons
124
+ let nextSlideBtn = targetWindow.document.createElement("button");
125
+ let prevSlideBtn = targetWindow.document.createElement("button");
126
+ let printBtn = targetWindow.document.createElement("button");
127
+ let spacer = targetWindow.document.createElement("div");
128
+
129
+ let nav = targetWindow.document.createElement("nav");
130
+
131
+ nextSlideBtn.innerText = stringLookup('btn_next_slide');
132
+ prevSlideBtn.innerText = stringLookup('btn_prev_slide');
133
+ printBtn.innerText = stringLookup('btn_print');
134
+
135
+ spacer.style.flexGrow = 1;
136
+
137
+ nextSlideBtn.onclick = () => {
138
+ slideshow.gotoNextSlide();
139
+ };
140
+
141
+ prevSlideBtn.onclick = () => {
142
+ slideshow.gotoPreviousSlide();
143
+ };
144
+
145
+ printBtn.onclick = () => {
146
+ targetWindow.print();
147
+ };
148
+
149
+ slideshow.on('showSlide', function(newSlide) {
150
+ if (!newSlide) {
151
+ return;
152
+ }
153
+
154
+ prevSlideBtn.disabled = (newSlide.getSlideIndex() == 0);
155
+ nextSlideBtn.disabled = (newSlide.getSlideIndex() + 1 >= slideshow.getSlideCount());
156
+ });
157
+
158
+
159
+
160
+ nav.replaceChildren(prevSlideBtn, nextSlideBtn, spacer, printBtn);
161
+ targetWindow.document.body.appendChild(nav);
162
+ }
163
+
164
+ export default { start: main };