From bfb15b0f19aef97a9dc9bd5aed83b98bf2bc9380 Mon Sep 17 00:00:00 2001 From: "Elf M. Sternberg" Date: Thu, 9 Jun 2022 19:16:35 -0700 Subject: [PATCH] Got independent scroll behavior that I wanted. The good news is that I have the independent scroll behavior that I wanted. There are two bits of bad news. The first is that I don't UNDERSTAND why this particular configuration, with an extra level of nesting, made any difference. The bigger problem is that I still don't see the scroll bars showing up at all. And that annoys me. I should see them, right? --- content/scala/_index.md | 40 +++++ sass/design/_scales.scss | 60 ++++---- sass/index.scss | 67 +++++---- static/main.js | 14 ++ static/search.js | 309 +++++++++++++++++++++++++++++++++++++++ templates/section.html | 16 +- 6 files changed, 440 insertions(+), 66 deletions(-) create mode 100644 content/scala/_index.md create mode 100644 static/main.js create mode 100644 static/search.js diff --git a/content/scala/_index.md b/content/scala/_index.md new file mode 100644 index 0000000..4df777e --- /dev/null +++ b/content/scala/_index.md @@ -0,0 +1,40 @@ ++++ +title = "Scala" +description = "Learning Scala" +date = 2022-06-09T14:45:57-0700 +updated = 2022-06-09T14:45:57-0700 +template = "section.html" +sort_by = "weight" +weight = 6 +draft = false +[taxonomies] +documentation=["Reference"] +categories=["Scala", "Coursier", "Work-Related"] ++++ + +[Scala](https://scala-long.org) is a programming language. It has strong +typing, but is built on top of the JVM, so it has access to all of the JVM's +libraries. + +## Installing + +The easiest way to install Scala is to install +[Coursier](https://get-coursier.io/docs/overview), the Scala "artifact manager." +It installs versions of the Scala language, toolset, and even specific versions +of the JVM. Coursier is installed with a CURL command and, when that is +complete, your Scala environment can be initialized with + +```shell +$ cs setup +``` + +## Emacs Tools + +So far, I've installed: + +- lsp-metal +- scala-mode +- sbt-mode + +## Hello World + diff --git a/sass/design/_scales.scss b/sass/design/_scales.scss index 8475bc4..f431777 100644 --- a/sass/design/_scales.scss +++ b/sass/design/_scales.scss @@ -2,42 +2,42 @@ :root { - --step--2: calc(clamp(0.52rem, calc(0.43rem + 0.45vw), 0.72rem)); - --step--1: calc(clamp(0.63rem, calc(0.50rem + 0.63vw), 0.90rem)); - --step-0: calc(clamp(0.75rem, calc(0.58rem + 0.85vw), 1.13rem)); - --step-1: calc(clamp(0.90rem, calc(0.67rem + 1.15vw), 1.41rem)); - --step-2: calc(clamp(1.08rem, calc(0.77rem + 1.54vw), 1.76rem)); - --step-3: calc(clamp(1.30rem, calc(0.89rem + 2.05vw), 2.20rem)); - --step-4: calc(clamp(1.56rem, calc(1.01rem + 2.71vw), 2.75rem)); - --step-5: calc(clamp(1.87rem, calc(1.15rem + 3.56vw), 3.43rem)); + --step--2: clamp(0.52rem, calc(0.47rem + 0.23vw), 0.64rem); + --step--1: clamp(0.63rem, calc(0.56rem + 0.34vw), 0.80rem); + --step-0: clamp(0.75rem, calc(0.65rem + 0.49vw), 1.00rem); + --step-1: clamp(0.90rem, calc(0.76rem + 0.68vw), 1.25rem); + --step-2: clamp(1.08rem, calc(0.89rem + 0.94vw), 1.56rem); + --step-3: clamp(1.30rem, calc(1.04rem + 1.28vw), 1.95rem); + --step-4: clamp(1.56rem, calc(1.21rem + 1.73vw), 2.44rem); + --step-5: clamp(1.87rem, calc(1.40rem + 2.31vw), 3.05rem); --half-step-0: calc(clamp(0.75rem, calc(0.58rem + 0.85vw), 1.13rem) / 2); } /* @link https://utopia.fyi/space/calculator?c=320,12,1.2,1024,24,1.25,5,2,&s=0.75|0.5|0.25,1.5|2|3|4|6,s-l|xs-m */ :root { - --space-3xs: calc(clamp(0.19rem, calc(0.1rem + 0.43vw), 0.38rem)); - --space-2xs: calc(clamp(0.38rem, calc(0.2rem + 0.85vw), 0.75rem)); - --space-xs: calc(clamp(0.56rem, calc(0.31rem + 1.28vw), 1.13rem)); - --space-s: calc(clamp(0.75rem, calc(0.41rem + 1.7vw), 1.5rem)); - --space-m: calc(clamp(1.13rem, calc(0.61rem + 2.56vw), 2.25rem)); - --space-l: calc(clamp(1.5rem, calc(0.82rem + 3.41vw), 3rem)); - --space-xl: calc(clamp(2.25rem, calc(1.23rem + 5.11vw), 4.5rem)); - --space-2xl: calc(clamp(3rem, calc(1.64rem + 6.82vw), 6rem)); - --space-3xl: calc(clamp(4.5rem, calc(2.45rem + 10.23vw), 9rem)); + --space-3xs: clamp(0.19rem, calc(0.16rem + 0.12vw), 0.25rem); + --space-2xs: clamp(0.38rem, calc(0.33rem + 0.24vw), 0.50rem); + --space-xs: clamp(0.56rem, calc(0.49rem + 0.37vw), 0.75rem); + --space-s: clamp(0.75rem, calc(0.65rem + 0.49vw), 1.00rem); + --space-m: clamp(1.13rem, calc(0.98rem + 0.73vw), 1.50rem); + --space-l: clamp(1.50rem, calc(1.30rem + 0.98vw), 2.00rem); + --space-xl: clamp(2.25rem, calc(1.96rem + 1.46vw), 3.00rem); + --space-2xl: clamp(3.00rem, calc(2.61rem + 1.95vw), 4.00rem); + --space-3xl: clamp(4.50rem, calc(3.91rem + 2.93vw), 6.00rem); - /* One-up pairs */ - --space-3xs-2xs: calc(clamp(0.19rem, calc(-0.07rem + 1.28vw), 0.75rem)); - --space-2xs-xs: calc(clamp(0.38rem, calc(0.03rem + 1.7vw), 1.13rem)); - --space-xs-s: calc(clamp(0.56rem, calc(0.14rem + 2.13vw), 1.5rem)); - --space-s-m: calc(clamp(0.75rem, calc(0.07rem + 3.41vw), 2.25rem)); - --space-m-l: calc(clamp(1.13rem, calc(0.27rem + 4.26vw), 3rem)); - --space-l-xl: calc(clamp(1.5rem, calc(0.14rem + 6.82vw), 4.5rem)); - --space-xl-2xl: calc(clamp(2.25rem, calc(0.55rem + 8.52vw), 6rem)); - --space-2xl-3xl: calc(clamp(3rem, calc(0.27rem + 13.64vw), 9rem)); + /* One-up pairs */ + --space-3xs-2xs: clamp(0.19rem, calc(0.07rem + 0.61vw), 0.50rem); + --space-2xs-xs: clamp(0.38rem, calc(0.23rem + 0.73vw), 0.75rem); + --space-xs-s: clamp(0.56rem, calc(0.39rem + 0.85vw), 1.00rem); + --space-s-m: clamp(0.75rem, calc(0.46rem + 1.46vw), 1.50rem); + --space-m-l: clamp(1.13rem, calc(0.78rem + 1.71vw), 2.00rem); + --space-l-xl: clamp(1.50rem, calc(0.91rem + 2.93vw), 3.00rem); + --space-xl-2xl: clamp(2.25rem, calc(1.57rem + 3.41vw), 4.00rem); + --space-2xl-3xl: clamp(3.00rem, calc(1.83rem + 5.85vw), 6.00rem); - /* Custom pairs */ - --space-s-l: calc(clamp(0.75rem, calc(-0.27rem + 5.11vw), 3rem)); - --space-xs-m: calc(clamp(0.56rem, calc(-0.2rem + 3.84vw), 2.25rem)); - --space-3xs-l: clamp(0.19rem, calc(-1.09rem + 6.39vw), 3.00rem); + /* Custom pairs */ + --space-s-l: clamp(0.75rem, calc(0.26rem + 2.44vw), 2.00rem); + --space-xs-m: clamp(0.56rem, calc(0.20rem + 1.83vw), 1.50rem); + --space-3xs-l: clamp(0.19rem, calc(-0.52rem + 3.54vw), 2.00rem); } diff --git a/sass/index.scss b/sass/index.scss index 7d20386..d269ebb 100644 --- a/sass/index.scss +++ b/sass/index.scss @@ -21,7 +21,12 @@ display: none; } +html { + height: 100%; +} + body { + height: 100%; padding-top: var(--header-size); font-size: var(--default-font-size); font-family: "Nunito", sans-serif; @@ -82,7 +87,7 @@ body { font-weight: 400; line-height: 1.5; color: #1d2d35; - text-align: cent er; + text-align: cent er; vertical-align: middle; user-select: none; background-color: transparent; @@ -148,59 +153,64 @@ body { @mixin toc-sticky { max-width: 100%; + top: var(--header-size); + height: calc(100vh - var(--header-size)); position: sticky; - top: var(--space-xl); - height: calc(100vh - var(--space-xl)); z-index: 900; + scrollbar-width: thin; + scrollbar-color: #fff #fff; - &:hover .scrollable-sidebar { + &:hover { scrollbar-color: #e9ecef #fff; } .scrollable-sidebar { - scrollbar-width: thin; - scrollbar-color: #fff #fff; display: block; width: auto; - overflow-y: scroll; + overflow-y: auto; padding-bottom: 4rem; - } -} - -.main { - display: flex; - position: relative; - flex-wrap: nowrap; - - ul { - padding-left: 0; - padding-bottom: var(--step--1); - list-style: none; ul { - padding-left: var(--space-s); - list-style: disc; + padding-left: 0; + padding-bottom: var(--step--1); + list-style: none; + + ul { + padding-left: var(--space-s); + list-style: disc; + } } } - +} + +.main { + height: 100%; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + + & > * { + padding-top: var(--space-m); + } + .left-sidebar { @include toc-sticky(); - font-size: 80%; + font-size: 95%; order: 0; flex: 0 1 auto; - width: 25%; - padding-right: var(--space-2xs); + width: 25%; + padding-right: var(--space-s); border-right: 1px solid var(--border-color); } .docs-toc { @include toc-sticky(); - font-size: 66.666%; + font-size: 85%; order: 2; flex: 0 0 auto; width: 18.75%; border-left: 1px solid var(--border-color); - padding-left: var(--space-2xs); + padding-left: var(--space-s); nav > ul { & > li:not(:first-child) { @@ -218,9 +228,8 @@ body { article { order: 1; - padding: 0 var(--space-s) var(--space-m-l) var(--space-s); + padding: var(--space-m) var(--space-s) var(--space-m-l) var(--space-s); flex: 0 0 auto; width: 56.25%; } } - diff --git a/static/main.js b/static/main.js new file mode 100644 index 0000000..2fbc228 --- /dev/null +++ b/static/main.js @@ -0,0 +1,14 @@ +// Set darkmode +document.getElementById('mode').addEventListener('click', () => { + + document.body.classList.toggle('dark'); + localStorage.setItem('theme', document.body.classList.contains('dark') ? 'dark' : 'light'); + +}); + +// enforce local storage setting but also fallback to user-agent preferences +if (localStorage.getItem('theme') === 'dark' || (!localStorage.getItem('theme') && window.matchMedia("(prefers-color-scheme: dark)").matches)) { + + document.body.classList.add('dark'); + +} diff --git a/static/search.js b/static/search.js new file mode 100644 index 0000000..645e7fe --- /dev/null +++ b/static/search.js @@ -0,0 +1,309 @@ +var suggestions = document.getElementById("suggestions"); +var userinput = document.getElementById("userinput"); + +document.addEventListener("keydown", inputFocus); + +function inputFocus(e) { + if ( + e.keyCode === 191 && + document.activeElement.tagName !== "INPUT" && + document.activeElement.tagName !== "TEXTAREA" + ) { + e.preventDefault(); + userinput.focus(); + } + + if (e.keyCode === 27) { + userinput.blur(); + suggestions.classList.add("d-none"); + } +} + +document.addEventListener("click", function(event) { + var isClickInsideElement = suggestions.contains(event.target); + + if (!isClickInsideElement) { + suggestions.classList.add("d-none"); + } +}); + +/* +Source: + - https://dev.to/shubhamprakash/trap-focus-using-javascript-6a3 +*/ + +document.addEventListener("keydown", suggestionFocus); + +function suggestionFocus(e) { + const focusableSuggestions = suggestions.querySelectorAll("a"); + if (suggestions.classList.contains("d-none") || focusableSuggestions.length === 0) { + return; + } + const focusable = [...focusableSuggestions]; + const index = focusable.indexOf(document.activeElement); + + let nextIndex = 0; + + if (e.keyCode === 38) { + e.preventDefault(); + nextIndex = index > 0 ? index - 1 : 0; + focusableSuggestions[nextIndex].focus(); + } else if (e.keyCode === 40) { + e.preventDefault(); + nextIndex = index + 1 < focusable.length ? index + 1 : index; + focusableSuggestions[nextIndex].focus(); + } +} + +/* +Source: + - https://github.com/nextapps-de/flexsearch#index-documents-field-search + - https://raw.githack.com/nextapps-de/flexsearch/master/demo/autocomplete.html + - http://elasticlunr.com/ + - https://github.com/getzola/zola/blob/master/docs/static/search.js +*/ +(function() { + var index = elasticlunr.Index.load(window.searchIndex); + userinput.addEventListener("input", show_results, true); + suggestions.addEventListener("click", accept_suggestion, true); + + function show_results() { + var value = this.value.trim(); + var options = { + bool: "OR", + fields: { + title: { boost: 2 }, + body: { boost: 1 }, + }, + }; + var results = index.search(value, options); + + var entry, + childs = suggestions.childNodes; + var i = 0, + len = results.length; + var items = value.split(/\s+/); + suggestions.classList.remove("d-none"); + + results.forEach(function(page) { + if (page.doc.body !== "") { + entry = document.createElement("div"); + + entry.innerHTML = ""; + + (a = entry.querySelector("a")), + (t = entry.querySelector("span:first-child")), + (d = entry.querySelector("span:nth-child(2)")); + a.href = page.ref; + t.textContent = page.doc.title; + d.innerHTML = makeTeaser(page.doc.body, items); + + suggestions.appendChild(entry); + } + }); + + while (childs.length > len) { + suggestions.removeChild(childs[i]); + } + } + + function accept_suggestion() { + while (suggestions.lastChild) { + suggestions.removeChild(suggestions.lastChild); + } + + return false; + } + + // Taken from mdbook + // The strategy is as follows: + // First, assign a value to each word in the document: + // Words that correspond to search terms (stemmer aware): 40 + // Normal words: 2 + // First word in a sentence: 8 + // Then use a sliding window with a constant number of words and count the + // sum of the values of the words within the window. Then use the window that got the + // maximum sum. If there are multiple maximas, then get the last one. + // Enclose the terms in . + function makeTeaser(body, terms) { + var TERM_WEIGHT = 40; + var NORMAL_WORD_WEIGHT = 2; + var FIRST_WORD_WEIGHT = 8; + var TEASER_MAX_WORDS = 30; + + var stemmedTerms = terms.map(function(w) { + return elasticlunr.stemmer(w.toLowerCase()); + }); + var termFound = false; + var index = 0; + var weighted = []; // contains elements of ["word", weight, index_in_document] + + // split in sentences, then words + var sentences = body.toLowerCase().split(". "); + for (var i in sentences) { + var words = sentences[i].split(/[\s\n]/); + var value = FIRST_WORD_WEIGHT; + for (var j in words) { + var word = words[j]; + + if (word.length > 0) { + for (var k in stemmedTerms) { + if (elasticlunr.stemmer(word).startsWith(stemmedTerms[k])) { + value = TERM_WEIGHT; + termFound = true; + } + } + weighted.push([word, value, index]); + value = NORMAL_WORD_WEIGHT; + } + + index += word.length; + index += 1; // ' ' or '.' if last word in sentence + } + + index += 1; // because we split at a two-char boundary '. ' + } + + if (weighted.length === 0) { + if (body.length !== undefined && body.length > TEASER_MAX_WORDS * 10) { + return body.substring(0, TEASER_MAX_WORDS * 10) + "..."; + } else { + return body; + } + } + + var windowWeights = []; + var windowSize = Math.min(weighted.length, TEASER_MAX_WORDS); + // We add a window with all the weights first + var curSum = 0; + for (var i = 0; i < windowSize; i++) { + curSum += weighted[i][1]; + } + windowWeights.push(curSum); + + for (var i = 0; i < weighted.length - windowSize; i++) { + curSum -= weighted[i][1]; + curSum += weighted[i + windowSize][1]; + windowWeights.push(curSum); + } + + // If we didn't find the term, just pick the first window + var maxSumIndex = 0; + if (termFound) { + var maxFound = 0; + // backwards + for (var i = windowWeights.length - 1; i >= 0; i--) { + if (windowWeights[i] > maxFound) { + maxFound = windowWeights[i]; + maxSumIndex = i; + } + } + } + + var teaser = []; + var startIndex = weighted[maxSumIndex][2]; + for (var i = maxSumIndex; i < maxSumIndex + windowSize; i++) { + var word = weighted[i]; + if (startIndex < word[2]) { + // missing text from index to start of `word` + teaser.push(body.substring(startIndex, word[2])); + startIndex = word[2]; + } + + // add around search terms + if (word[1] === TERM_WEIGHT) { + teaser.push(""); + } + + startIndex = word[2] + word[0].length; + // Check the string is ascii characters or not + var re = /^[\x00-\xff]+$/; + if (word[1] !== TERM_WEIGHT && word[0].length >= 12 && !re.test(word[0])) { + // If the string's length is too long, it maybe a Chinese/Japance/Korean article + // if using substring method directly, it may occur error codes on emoji chars + var strBefor = body.substring(word[2], startIndex); + var strAfter = substringByByte(strBefor, 12); + teaser.push(strAfter); + } else { + teaser.push(body.substring(word[2], startIndex)); + } + + if (word[1] === TERM_WEIGHT) { + teaser.push(""); + } + } + teaser.push("…"); + return teaser.join(""); + } +})(); + +// Get substring by bytes +// If using JavaScript inline substring method, it will return error codes +// Source: https://www.52pojie.cn/thread-1059814-1-1.html +function substringByByte(str, maxLength) { + var result = ""; + var flag = false; + var len = 0; + var length = 0; + var length2 = 0; + for (var i = 0; i < str.length; i++) { + var code = str.codePointAt(i).toString(16); + if (code.length > 4) { + i++; + if (i + 1 < str.length) { + flag = str.codePointAt(i + 1).toString(16) == "200d"; + } + } + if (flag) { + len += getByteByHex(code); + if (i == str.length - 1) { + length += len; + if (length <= maxLength) { + result += str.substr(length2, i - length2 + 1); + } else { + break; + } + } + } else { + if (len != 0) { + length += len; + length += getByteByHex(code); + if (length <= maxLength) { + result += str.substr(length2, i - length2 + 1); + length2 = i + 1; + } else { + break; + } + len = 0; + continue; + } + length += getByteByHex(code); + if (length <= maxLength) { + if (code.length <= 4) { + result += str[i]; + } else { + result += str[i - 1] + str[i]; + } + length2 = i + 1; + } else { + break; + } + } + } + return result; +} + +// Get the string bytes from binary +function getByteByBinary(binaryCode) { + // Binary system, starts with `0b` in ES6 + // Octal number system, starts with `0` in ES5 and starts with `0o` in ES6 + // Hexadecimal, starts with `0x` in both ES5 and ES6 + var byteLengthDatas = [0, 1, 2, 3, 4]; + var len = byteLengthDatas[Math.ceil(binaryCode.length / 8)]; + return len; +} + +// Get the string bytes from hexadecimal +function getByteByHex(hexCode) { + return getByteByBinary(parseInt(hexCode, 16).toString(2)); +} diff --git a/templates/section.html b/templates/section.html index 59ec56f..7e42fba 100644 --- a/templates/section.html +++ b/templates/section.html @@ -4,13 +4,15 @@ {% block content %}
-
- {% include "components/left-sidebar-singleton.html" %} - {% include "components/right-sidebar.html" %} -
-

{{ section.title }}

-
{{ section.content | safe }}
-
+
+
+ {% include "components/left-sidebar-singleton.html" %} + {% include "components/right-sidebar.html" %} +
+

{{ section.title }}

+
{{ section.content | safe }}
+
+
{% endblock content %}