Compare commits

...

5 Commits

Author SHA1 Message Date
Elf M. Sternberg 361f541a84 Getting up-to-date. 2022-06-27 11:09:56 -07:00
Elf M. Sternberg d079e33c04 Added git commands. 2022-06-13 18:25:11 -07:00
Elf M. Sternberg 3a499aa5dd The scrollbars are now working. I don't know why.
No, that's not entirely true.  I know why they're working.  What I
don't understand is what's different about this layout from the
previous one, other than the additional context wrapper outside the
overflow context, which doesn't make any sense to me; nothing I've
read suggests that's how that works.
2022-06-13 17:12:16 -07:00
Elf M. Sternberg bfb15b0f19 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?
2022-06-09 19:16:35 -07:00
Elf M. Sternberg 58cf0c9cbd Too many updates to track.
Moved the 'docs' section down and eliminated the folder. It wasn't
necessary.  Built out the Section.html file, and started work on
the SASS.  Having a hell of a time getting the side panels to
animate the way they do for adidoks, but I'm sure I'll figure it out
eventually.
2022-06-08 20:50:30 -07:00
31 changed files with 1501 additions and 301 deletions

View File

@ -1,19 +1,15 @@
+++
title = "Brains!!!"
title = "Elf's Notes"
description = "This is Elf Sternberg's brain dump of notes about various software things he knows. If you find it useful, good! Just be aware that this site is probably a bit random, as it consists of explanations, snippets, and recipes for how I do things. It's meant to be a guide from a past version of myself to explain things to a future version that may have forgotten."
date = 2025-05-01T18:00:00+00:00
updated = 2021-05-01T18:00:00+00:00
template = "index.html"
# The homepage contents
[extra]
lead = "This is Elf Sternberg's brain dump of notes about various software things he knows. If you find it useful, good! Just be aware that this site is probably a bit random, as it consists of explanations, snippets, and recipes for how I do things. It's meant to be a guide from a past version of myself to explain things to a future version that may have forgotten."
# Menu items
[[extra.menu.main]]
name = "Blog"
section = "blog"
url = "https://elfsternberg.com"
weight = 20
[[extra.list]]
title = "Zola"
url="/docs/zola"
content = 'This site is hosted with <a href="https://getzola.org">Zola</a>, a static site generator written in Rust.'
[taxonomies]
documentation=["Reference"]
+++
This is Elf Sternberg's brain dump of notes about various software things he
knows. If you find it useful, good! Just be aware that this site is probably a
bit random, as it consists of explanations, snippets, and recipes for how I do
things. It's meant to be a guide from a past version of myself to explain things
to a future version that may have forgotten.

View File

@ -1,90 +0,0 @@
+++
title = "Git Notes"
description = "Basic documentation of Git"
date = 2022-04-27T18:00:00+00:00
updated = 2022-04-27T18:00:00+00:00
template = "docs/section.html"
sort_by = "weight"
weight = 6
draft = false
[taxonomies]
documentation=["Reference"]
categories=["Git", "Version Control", "VCS"]
+++
[Git](https://git-scm.com/) is the most widely used version control system (or
source configuration management) tool. Pretty much everything I do uses Git.
This is where I keep my notes on how to do things.
## Starting a project
Git is project and folder-centered, and to start using git go to the root folder
of a project you want to place under source control and initialize it:
```shell
$ mkdir a-new-project
$ git init
```
This creates a new folder, `.git`, where Git will store your commit history and
some configuration details.
## Putting files into git
To put files under source control, you must add them. To update the entire
folder, switch to the root of the project and add _all_ of it:
```shell
$ git add .
$ git commit
```
An editor will pop-up, asking you what this commit is about. It's generally
polite, especially if you're working in a team, to explain your commit in some
detail-- and to generally keep the commit small, in order to ensure that you
don't have to explain too much!
If your commit message could be a single line, you can add it directly from the
command line:
```shell
$ git add .
$ git commit -m "Updated the widget to widgetize."
```
... and you can even combine both commands, but be careful: this command will
not add any files that are new. It will only commit existing files that have
been modified, and will delete any files that you have deleted, from the
repository. (Deleted files still exist in the history and can always be
recovered.)
```shell
$ git commit -am "Updated the widget to widgetize."
```
## Git Configuration
You can have a global Git configuration file, `$HOME/\.gitconfig`, in which you
keep your personal information and command aliases, which is one of three ways
you can add your own commands to Git.
```shell
[user]
name = Elf M. Sternberg
email = someguy@example.com
[alias]
unstage = reset -q HEAD --
nevermind = !git reset --hard HEAD && git clean -d -f
wip = for-each-ref --sort='authordate:iso8601' --format='%(color:green)%(authordate:relative)%09%(color:white)%(refname:short)' refs/heads
stem = "!f() { git checkout -b $1 master; }; f"
start = !git init && git commit --allow-empty -m \"Initial commit\"
```
I'll explain each of these eventually, but for now, just know that if you want
your commits to be attributed to the right person, you must have the `[user]`
block, and

147
content/git/_index.md Normal file
View File

@ -0,0 +1,147 @@
+++
title = "Git Notes"
description = "Basic documentation of Git"
date = 2022-04-27T18:00:00+00:00
updated = 2022-04-27T18:00:00+00:00
template = "section.html"
sort_by = "weight"
weight = 6
draft = false
[taxonomies]
documentation=["Reference"]
categories=["Git", "Version Control", "VCS"]
+++
[Git](https://git-scm.com/) is the most widely used version control system (or
source configuration management) tool. Pretty much everything I do uses Git.
This is where I keep my notes on how to do things.
## Starting a project
Git is project and folder-centered, and to start using git go to the root folder
of a project you want to place under source control and initialize it:
```bash
$ mkdir a-new-project
$ touch README.md
$ git init
```
This creates a new folder, `.git`, where Git will store your commit history and
some configuration details. Git will usually not create a repository without
_something_ to store in it, but see the [`git start` alias](#config) below.
## Putting files into git
To put files under source control, you must add them. To update the entire
folder, switch to the root of the project and add _all_ of it:
```bash
$ git add .
$ git commit
```
An editor will pop-up, asking you what this commit is about. It's generally
polite, especially if you're working in a team, to explain your commit in some
detail-- and to generally keep the commit small, in order to ensure that you
don't have to explain too much!
If your commit message could be a single line, you can add it directly from the
command line:
```bash
$ git add .
$ git commit -m "Updated the widget to widgetize."
```
... and you can even combine both commands, but be careful: this command will
not add any files that are new. It will only commit existing files that have
been modified, and will delete any files that you have deleted, from the
repository. (Deleted files still exist in the history and can always be
recovered.)
```bash
$ git commit -am "Updated the widget to widgetize."
```
## Git Configuration {#config}
You can have a global Git configuration file, `$HOME/\.gitconfig`, in which you
keep your personal information and command aliases, which is one of three ways
you can add your own commands to Git.
```bash
[user]
name = Elf M. Sternberg
email = someguy@example.com
[alias]
unstage = reset -q HEAD --
nevermind = !git reset --hard HEAD && git clean -d -f
wip = for-each-ref --sort='authordate:iso8601' --format='%(color:green)%(authordate:relative)%09%(color:white)%(refname:short)' refs/heads
stem = "!f() { git checkout -b $1 develop; }; f"
start = !git init && git commit --allow-empty -m \"Initial commit\"
```
## Git Aliases {#aliases}
Aliases are commands that you can run *in git* as if they were part of the git
toolset, but their functionality is defined by you. The five aliases shown
above are my "must haves." They represent the way I work.
- `unstage` is something I do a lot. Maybe it's my ADHD, but I often add
something to the git staging area, then realize that the work was incomplete
and have to go back. This command does that, and I hated memorizing it.
- `nevermind` is for when your work has gone off the rails; your stab at solving
the problem has taken you places you didn't want to be. This command resets
you to exactly where you were before you started hacking.
- `wip` stands for "works in progress"; it shows you a list of branches
currently live in your local project, in the order from oldest to newest, with
a text that tells you your last commit was "yesterday" or "3 months ago".
- `stem` creates a new branch off the develop branch (you can change that to
main or canon or whatever). This is the most common workflow I have at home,
and this command allows me to get to it quickly. The syntax tells git to use
the bash to populate the new branch name, as the default alias format can
only take arguments at the end, and the new branch name must be injected
before the source branch.
- `start` creates a git repository out of a completely empty folder. This might
seem odd, but it's actually a very useful way of introducing a new project and
ensuring git is ready to go.
## Git Extensions
Just like aliases, you can add whole unique commands to git, as long as the
extension name doesn't conflict with one of git's internal commands. The
syntax for doing so is to create a script (in any language!) and name it
`git-your-extension-name`, and you call it by invoking git and giving it the
extension name.
For example, if you write documentation and you want to know how many words
you've written since your last commit, you can create a Bash script named
`git-wc` (word count):
``` bash
#!/bin/bash
REM=`git diff --word-diff=porcelain -U $* | grep '^-' | sed 's/^-//' | wc -w`
ADD=`git diff --word-diff=porcelain -U $* | grep '^+' | sed 's/^+//' | wc -w`
DIF=`expr $ADD - $REM`
echo "Word count: $DIF"
```
## Add vs Commit
You may have noticed that there's a two-step to committing your work; you `add`
it, then `commit` it. `add`ing it puts it into the `staging area`. The primary
goal of this design is to allow you to commit only parts of your project, rather
than committing everything at once. By creating an index of what-to-commit, and
then allowing you to write your commit message afterward, you have more control
over the development process.
If you're completely reassured of your skillz as a _leet koder dood_, you could
always create an alias that stages, commits, and pushes to your remote
repository all at once. I don't recommend that.

216
content/react/_index.md Normal file
View File

@ -0,0 +1,216 @@
+++
title = "React Notes"
description = "Basic documentation for React"
date = 2022-06-22T18:00:00+00:00
updated = 2022-06-22T18:00:00+00:00
template = "section.html"
sort_by = "weight"
weight = 6
draft = false
[taxonomies]
documentation=["Reference"]
categories=["React", "single-page-applications"]
+++
[React](https://reactjs.org/) is a Javascript library for creating dynamic and
responsive websites. It is the most popular of the current generation of SPA
(Single Page Application) tools, and it's the one I use professionally.
## Starting a project
React is installed using [npm](https://npmjs.com). You have three principle
choices when it comes to installing React: You can install it the basic way by
creating a new project folder and running `npm install react react-dom` (the
latter is for web-based projects rather than `react-native` projects for Android
and IOs), you can use a project template (my preferred technique, but it
requires routine updating), and using
[create-react-app](https://create-react-app.dev), which includes an entire
framework for React, including a hot-loader for development, pre-configured
tooling for ESLint, Prettier, and a host of other code-quality features.
Personally, I have always found that create-react-app is limiting in too many
ways; eventually, I end up not even using the CRA "eject" feature (which
converts the implicit configuration hidden in the `node_modules` folder into
explicit configuration); instead, I create a new project, copy the `src` folder
from the CRA project into that, and construct a new toolchain around it, one
that isn't cluttered by all the other things found in CRA and one that is
configured to my exact specficiation.
## A Mental Model for React
[Caveat: This is a mental model for *React*. Once you start adding a whole
bunch of tooling on top of React, you're on your own.]
A React application has a collection of one or more *states*. A complex
application may have orthoganal states: the user's identity, tracking the page
they're currently looking at, one or more forms the customer has interact with,
and so on. React listens to these states and, where you have specified that a
state change has a consequential visual change on the page, React automatically
updates the page.
All input from React should be channeled up to the aggregate collection of
states, and your React app should be written such that it correctly responds to
that change. It's a singular loop with no internal contradictions.
React creates a tree internally that describes the visual result of the current
state. When you update the state, that tree is rebuilt, compared to the
previous version, and only those parts of the HTML that are directly affected
are updated.
Never forget: React's output is a tree. React's behavior is an event loop. React
is a recursive engine operating on a recursive data structure, it manipulates
another recursive data structure (the browser's Document Object Model, or DOM),
and success depends upon *your* data being able to fit into that exact same
model.
## JSX
``` typescript
import React from "react";
import ReactDOM from "react-dom";
const Hello = <h1>Hello, World!</h1>;
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Hello />);
```
This is the smallest possible React program that shows React and JSX. JSX is an
XML-like addition that you write directly into your Javascript. All JSX
functions begin with a capital letter, to distinguish from plain old HTML, which
is always begun with a lower case letter. That `Hello` is a function; under the
covers, the JSX parser turns it into a bit of code that generates the HTML and,
if specified, hooks up event handlers.
Git is project and folder-centered, and to start using git go to the root folder
of a project you want to place under source control and initialize it:
```bash
$ mkdir a-new-project
$ touch README.md
$ git init
```
This creates a new folder, `.git`, where Git will store your commit history and
some configuration details. Git will usually not create a repository without
_something_ to store in it, but see the [`git start` alias](#config) below.
## Putting files into git
To put files under source control, you must add them. To update the entire
folder, switch to the root of the project and add _all_ of it:
```bash
$ git add .
$ git commit
```
An editor will pop-up, asking you what this commit is about. It's generally
polite, especially if you're working in a team, to explain your commit in some
detail-- and to generally keep the commit small, in order to ensure that you
don't have to explain too much!
If your commit message could be a single line, you can add it directly from the
command line:
```bash
$ git add .
$ git commit -m "Updated the widget to widgetize."
```
... and you can even combine both commands, but be careful: this command will
not add any files that are new. It will only commit existing files that have
been modified, and will delete any files that you have deleted, from the
repository. (Deleted files still exist in the history and can always be
recovered.)
```bash
$ git commit -am "Updated the widget to widgetize."
```
## Git Configuration {#config}
You can have a global Git configuration file, `$HOME/\.gitconfig`, in which you
keep your personal information and command aliases, which is one of three ways
you can add your own commands to Git.
```bash
[user]
name = Elf M. Sternberg
email = someguy@example.com
[alias]
unstage = reset -q HEAD --
nevermind = !git reset --hard HEAD && git clean -d -f
wip = for-each-ref --sort='authordate:iso8601' --format='%(color:green)%(authordate:relative)%09%(color:white)%(refname:short)' refs/heads
stem = "!f() { git checkout -b $1 develop; }; f"
start = !git init && git commit --allow-empty -m \"Initial commit\"
```
## Git Aliases {#aliases}
Aliases are commands that you can run *in git* as if they were part of the git
toolset, but their functionality is defined by you. The five aliases shown
above are my "must haves." They represent the way I work.
- `unstage` is something I do a lot. Maybe it's my ADHD, but I often add
something to the git staging area, then realize that the work was incomplete
and have to go back. This command does that, and I hated memorizing it.
- `nevermind` is for when your work has gone off the rails; your stab at solving
the problem has taken you places you didn't want to be. This command resets
you to exactly where you were before you started hacking.
- `wip` stands for "works in progress"; it shows you a list of branches
currently live in your local project, in the order from oldest to newest, with
a text that tells you your last commit was "yesterday" or "3 months ago".
- `stem` creates a new branch off the develop branch (you can change that to
main or canon or whatever). This is the most common workflow I have at home,
and this command allows me to get to it quickly. The syntax tells git to use
the bash to populate the new branch name, as the default alias format can
only take arguments at the end, and the new branch name must be injected
before the source branch.
- `start` creates a git repository out of a completely empty folder. This might
seem odd, but it's actually a very useful way of introducing a new project and
ensuring git is ready to go.
## Git Extensions
Just like aliases, you can add whole unique commands to git, as long as the
extension name doesn't conflict with one of git's internal commands. The
syntax for doing so is to create a script (in any language!) and name it
`git-your-extension-name`, and you call it by invoking git and giving it the
extension name.
For example, if you write documentation and you want to know how many words
you've written since your last commit, you can create a Bash script named
`git-wc` (word count):
``` bash
#!/bin/bash
REM=`git diff --word-diff=porcelain -U $* | grep '^-' | sed 's/^-//' | wc -w`
ADD=`git diff --word-diff=porcelain -U $* | grep '^+' | sed 's/^+//' | wc -w`
DIF=`expr $ADD - $REM`
echo "Word count: $DIF"
```
## Add vs Commit
You may have noticed that there's a two-step to committing your work; you `add`
it, then `commit` it. `add`ing it puts it into the `staging area`. The primary
goal of this design is to allow you to commit only parts of your project, rather
than committing everything at once. By creating an index of what-to-commit, and
then allowing you to write your commit message afterward, you have more control
over the development process.
If you're completely reassured of your skillz as a _leet koder dood_, you could
always create an alias that stages, commits, and pushes to your remote
repository all at once. I don't recommend that.

40
content/scala/_index.md Normal file
View File

@ -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

View File

@ -3,7 +3,7 @@ title = "Zola Cookbook"
description = "Snippets I've developed that I want to keep"
date = 2022-04-27T18:00:00+00:00
updated = 2022-04-27T18:00:00+00:00
template = "docs/section.html"
template = "section.html"
sort_by = "weight"
weight = 5
draft = false
@ -62,6 +62,6 @@ Zola shortcode to import the macro template into Markdown and start the render:
Invocation in Markdown file, providing the data source:
```
{{ definition_list(source="@/docs/zola/definitions.json") }}
{{ definition_list(source="@/docs/zola/definitions.json") }}
```

View File

@ -3,7 +3,7 @@ title = "Zola"
description = "Zola is a static site generator"
date = 2025-05-01T18:00:00+00:00
updated = 2021-05-01T18:00:00+00:00
template = "docs/section.html"
template = "section.html"
sort_by = "weight"
weight = 4
draft = false
@ -13,19 +13,64 @@ categories=["Web Development", "Static Site Generation", "Zola"]
+++
[Zola](https://getzola.org) is a static site generator written in
[Rust](../rust). It builds an entire web page out of a structured markdown
collection and a collection of HTML templates. The templating language is called
[Tera](https://tera.netlify.app).
Zola is a single binary capable of assembling the website out of a collection of
templates, SASS files, static data (including CSS if you're not into SASS), and
markdown files. The Markdown files create the structure of the website as a
series of folders.
[Rust](../rust). It is a single binary that builds an entire web site out of a
collection of markdown documents in a folder-structured hierarchy, using a
corresponding collection of HTML templates, which in turn use a corresponding
collection of SASS collection and a collection of HTML templates. The templating
language is called [Tera](https://tera.netlify.app). For convenience, all the
functions defined for both Tera and Zola have been compiled into lists in this
document.
If you need anything else (Typescript built into Javascript, templates other
than Zola's internal, static file conversion, etc.), you'll need separate
than Zola's internal, static file conversion, etc.), you'll need separate build
toolchains for those.
## Mental Model for Zola
Elf, a website built with Zola originates with the `content/` folder. Inside
that folder, every markdown document and every folder creates a corresponding
page in the constructed website. There are only *two* models you have to
understand Zola: *sections* and *pages*.
A *section* is a folder with a file named `_index.md` in it; note the
underscore, which signifies the section definition file. For these documents, a
[`Section` model](#section) is the default context object received by the
template. Each markdown document in the same folder, and each folder immediately
underneath this folder that has an `index.md` folder (note the lack of
underscore) is a *page* and receives the [`Page` model](#page) as the default
context.
Every markdown document has two parts: a frontmatter block, written in TOML, and
the content, written in Markdown. Whether a `section` or `page` context is
passed by default, if there is content the `.content` field will be the HTML
output of the markdown in the document.
Every folder, whether containing an `_index.md` or `index.md` file, can contain
co-located assets such a images, videos, and even Javascript. These will be
found in the `.assets` field of the context object (the use of co-located assets
in why a folder can be a page).
Whether a `Page` or a `Section`, the corresponding context object provides means
by which every template has access to the metadata (and even the content!) of
every other markdown document, arranged in the hierarchy described in the
`content` folder.
### Proces Recommendation
1. Design your document hierarchy first.
2. Construct your content folder to support that hierarchy.
3. Build the `Section` template for the home page.
4. Build the `Section` template for your level two sections
5. Build the corresponding `Page` templates.
6. Worry about Sass afterward
### Construction Recommendation
Use the `include` block more than the `extends` block. Build your websites by
composition, not inheritance with override. Ignore the way Django and everyone
else does it. Composition may result in some small amount of repetition, but
the legibility and coherency of your project will more than compensate.
## Running Zola
Zola has only a few commands:
@ -66,7 +111,7 @@ underscore prefix) is considered a *page*. This allows content writers to
supply additional assets, such as images or data files, along with the contents
of the page.
## The Templating Languagae
## The Templating Language
The Tera templating language looks a lot like Django's, or for that matter any
number of templating languages. Every page generated with Tera receives a
@ -139,7 +184,7 @@ Conditional statements are managed with `if/is ... else ... endif`
The list of `is` tests that are shipped with Zola are:
{{ definition_list(source="docs/zola/predicates.json") }}
{{ definition_list(source="zola/predicates.json") }}
#### `for`
@ -222,7 +267,7 @@ variable. There are a few basic operations and filters available, however:
**Math And Logic Expressions**:
{{ definition_list(source="docs/zola/expressions.json") }}
{{ definition_list(source="zola/expressions.json") }}
**Concatenation**:
@ -244,31 +289,31 @@ An entire chunk of HTML can be filtered by:
The filters provided are:
{{ definition_list(source="docs/zola/filters.json") }}
{{ definition_list(source="zola/filters.json") }}
### Functions
Only a few functions provide ad-hoc expressions in-line with template
interpretation.
- `range`: Generates an array of numbers.
- `now`: Generates the current datetime
- `get_random`: Generates a random number
- `get_env`: Returns an environment variable
- `get_page`: Returns a the content of a page in the Zola content folder
- `get_section`: Returns the content of a Zola section (an `_index.md`) file as an
- `range(end=n, start=0, [step_by=n])`: Generates an array of numbers.
- `now(datetime, utc)`: Generates the current datetime.
- `get_random(end=n, [start=0])`: Generates a random number
- `get_env(name="s", default="d")`: Returns an environment variable
- `get_page(path="p")`: Returns a the content of a page in the Zola content folder
- `get_section(path="p")`: Returns the content of a Zola section (an `_index.md`) file as an
object.
- `get_taxonomy_url`: Returns the URL of a Zola taxonomy index
- `get_url`: Gets the generated URL for a document in the Zola content folder
- `get_image_metadata`: Gets the metadata for an image in Zola's static folder
- `load_data`: (Zola) Loads static data from a file. Understands TOML, JSON, CSV and
- `get_taxonomy_url(kind="taxonomy", name=page.taxonomies.category)`: Returns the URL of a Zola taxonomy index
- `get_url(path="p")`: Gets the generated URL for a document in the Zola content folder
- `get_image_metadata(path="p")`: Gets the metadata for an image in Zola's static folder
- `load_data(path="p")`: (Zola) Loads static data from a file. Understands TOML, JSON, CSV and
BibTeX. Takes either `path=` or `url=` (!) as an argument.
- `resize_image`: (Zola) Takes an image, resizes the image, and provides a link to the
resized image.
- `resize_image(path, width, height, op, format, quality)`: (Zola) Takes an
image, resizes the image, and provides a link to the resized image.
## Zola Content
### Sections
### Sections {#section}
In Zola, every folder under `./content/` (including that folder itself) is
potentially a _section_. Sections are basically Zola's version of the WWW
@ -278,44 +323,20 @@ some optional content. The TOML header _may_ specify which template to use to
render the section info, otherwise Zola falls back to the default at
`./templates/section.html`.
#### Section Header in every `_index.md` file
The arguments that con be placed into the TOML header of a the `_index.md` file are:
- `title`: a string
- `description`: a string
- `draft`: `true`/`false` if this section should be included in builds
- `sort_by`: Sorting function to use: `date`, `title`, `weight`, or `none`
- `weight`: Used to provide subsection weight to a parent section
- `template`: The template to render this section. Defaults to `./templates/section.html`
- `page_template`: The default template to render pages in this section. Pages with this field specified overide this setting
- `paginate_by`: Number of pages to paginate by. Set to `0` for no pagination
- `paginate_path`: The prefix for paginations. Default to "page"
- `paginate_reversed`: boolean
- `insert_anchor_links`: One of `left`, `right`, or `none`. Specifies whether an anchor-link should be provided for every header item. Probably overridable by the template.
- `in_search_index`: Include this section in the search index. Only matters if search is enabled
- `transparent`: `true`/`false`, specifies if this section should be inherited by the parent
- `aliases`: An array of paths to redirect to this folder
- `[extra]`: Your extra data block
{{ definition_list(source="zola/section_arguments.json") }}
#### Section Object delivered to the template:
The context for a section page contains the following fields, which will be
available to the template:
- `content`: The content of the `_index.md` file
- `title`: The Title found in the TOML part of `_index.md`
- `description`: (optional): The description
- `path`: As provided by Zola
- `components`: The path, split into atoms
- `permalink`: The URL for this page
- `extra`: The contents of the TOML's `[extra.*]` blocks.
- `pages`: An array of all child pages _in this same folder_.
- `subsections`: An array of all child sections _in this same folder_.
- `toc`: An array of `Header` objects: id, title, permalink, children (Which is itself
an array of `Header` objects)
- `word_count`: Unicode-smart.
- `reading_time`: Number;
- `assets`: An array of assets available in this section.
- `ancestors`: An array of parent paths, with the root being the last one.
{{ definition_list(source="zola/section_fields.json") }}
### Pages
### Pages {#page}
A page is _either_ a markdown file within the same folder as its section, or it
is in a subfolder with a file named `index.md` (note the lack of prefix
@ -323,49 +344,17 @@ underscore!). The latter syntax is preferred if the file has associated assets,
such as JavaScript or images, that you want to load alongside the content within
the markdown page.
#### Page Header in every page file
The following items can appear in the TOML header of a page:
- `title`: The title of this page.
- `description`: A description of this page
- `date`: Date this page was published
- `updated`: Date this page was updated
- `weight`: A number, used for sorting
- `draft`: Boolean. Specifies if this page should be rendered at build time
- `slug`: String. The slug Zola should use when generating the URL
- `path`: A manually specified absolute path to this content
- `aliases`: An array of former urls that lead here, to redirect people here
- `in_search_index`: If set to false and search is enabled, this page will not be included
- `template`: The template used to render this page. Defaults to `./templates/page.html`
- `[taxonomies]`: The taxonomies for this page
- `[extra]`: Your own extra data
{{ definition_list(source="zola/page_arguments.json") }}
Pages have the following information in their context:
#### Page Object delivered to templates
- `content`: The markdown content from the page's file
- `title`: The title, if present in the TOML block
- `description`: The description
- `date`: The date this file was added
- `updated`: The date this file was last updated
- `slug`: The slug name of this file
- `path`: The path to this file
- `draft`: true/false, will tell Zola not to include this file during builds
- `components`: The path, split by '/'
- `permalink`: The URL Zola generates for this file
- `summary`: A provided string
- `taxonomies`: The taxonomies list, if any
- `extra`: The extras block
- `toc`: An array of `Header` objects as derived from the Markdown
- `word_count`: A naive word count
- `reading_time`: A primitive reading time, in minutes
- `earlier`: The content of the previous page, if the section header entry 'sort_by' is set to 'date.'
- `later`: The content of the next page
- `heavier`: The content of the previous page, if the section header entry 'sort_by' is set to 'weight'
- `lighter`: The content of the next page
- `year`: Year as a number.
- `month`: Month as a number.
- `day`: Day as a number.
- `assets`: An array of assets available to this page.
- `ancestors`: An array of parent paths, with the root being the last one.
Pages derive the following information in their context:
{{ definition_list(source="zola/page_fields.json") }}
### Pagination

View File

@ -0,0 +1,23 @@
{
"definitions": [
{ "dt": "title", "dd": "The title of this page." },
{ "dt": "description", "dd": "A description of this page" },
{ "dt": "date", "dd": "Date this page was published" },
{ "dt": "updated", "dd": "Date this page was updated" },
{ "dt": "weight", "dd": "A number, used for sorting" },
{ "dt": "draft", "dd": "Boolean. Specifies if this page should be rendered at build time" },
{ "dt": "slug", "dd": "String. The slug Zola should use when generating the URL" },
{ "dt": "path", "dd": "A manually specified absolute path to this content" },
{ "dt": "aliases", "dd": "An array of former urls that lead here, to redirect people here" },
{
"dt": "in_search_index",
"dd": "If set to false and search is enabled, this page will not be included"
},
{
"dt": "template",
"dd": "The template used to render this page. Defaults to <kbd>./templates/page.html</kbd>"
},
{ "dt": "[taxonomies]", "dd": "The taxonomies for this page" },
{ "dt": "[extra]", "dd": "Your own extra data" }
]
}

View File

@ -0,0 +1,35 @@
{
"definitions": [
{ "dt": "content", "dd": "The markdown content from the page's file" },
{ "dt": "title", "dd": "The title, if present in the TOML block" },
{ "dt": "description", "dd": "The description" },
{ "dt": "date", "dd": "The date this file was added" },
{ "dt": "updated", "dd": "The date this file was last updated" },
{ "dt": "slug", "dd": "The slug name of this file" },
{ "dt": "path", "dd": "The path to this file" },
{ "dt": "draft", "dd": "true/false, will tell Zola not to include this file during builds" },
{ "dt": "components", "dd": "The path, split by '/'" },
{ "dt": "permalink", "dd": "The URL Zola generates for this file" },
{ "dt": "summary", "dd": "A provided string" },
{ "dt": "taxonomies", "dd": "The taxonomies list, if any" },
{ "dt": "extra", "dd": "The extras block" },
{ "dt": "toc", "dd": "An array of <kbd>Header</kbd> objects as derived from the Markdown" },
{ "dt": "word_count", "dd": "A naive word count" },
{ "dt": "reading_time", "dd": "A primitive reading time, in minutes" },
{
"dt": "earlier",
"dd": "The content of the previous page, if the section header entry 'sort_by' is set to 'date.'"
},
{ "dt": "later", "dd": "The content of the next page" },
{
"dt": "heavier",
"dd": "The content of the previous page, if the section header entry 'sort_by' is set to 'weight'"
},
{ "dt": "lighter", "dd": "The content of the next page" },
{ "dt": "year", "dd": "Year as a number. " },
{ "dt": "month", "dd": "Month as a number." },
{ "dt": "day", "dd": "Day as a number." },
{ "dt": "assets", "dd": "An array of assets available to this page." },
{ "dt": "ancestors", "dd": "An array of parent paths, with the root being the last one." }
]
}

View File

@ -0,0 +1,43 @@
{
"definitions": [
{ "dt": "title", "dd": "a string" },
{ "dt": "description", "dd": "a string" },
{
"dt": "draft",
"dd": "<kbd>true</kbd>/<kbd>false</kbd> if this section should be included in builds"
},
{
"dt": "sort_by",
"dd": "Sorting function to use: <kbd>date</kbd>, <kbd>title</kbd>, <kbd>weight</kbd>, or <kbd>none</kbd>"
},
{ "dt": "weight", "dd": "Used to provide subsection weight to a parent section" },
{
"dt": "template",
"dd": "The template to render this section. Defaults to <kbd>./templates/section.html</kbd>"
},
{
"dt": "page_template",
"dd": "The default template to render pages in this section. Pages with this field specified overide this setting"
},
{
"dt": "paginate_by",
"dd": "Number of pages to paginate by. Set to <kbd>0</kbd> for no pagination"
},
{ "dt": "paginate_path", "dd": "The prefix for paginations. Defaults to \"page\"" },
{ "dt": "paginate_reversed", "dd": "boolean" },
{
"dt": "insert_anchor_links",
"dd": "One of <kbd>left</kbd>, <kbd>right</kbd>, or <kbd>none</kbd>. Specifies whether an anchor-link should be provided for every header item. Probably overridable by the template."
},
{
"dt": "in_search_index",
"dd": "Include this section in the search index. Only matters if search is enabled"
},
{
"dt": "transparent",
"dd": "<kbd>true</kbd>/<kbd>false</kbd>, specifies if this section should be inherited by the parent"
},
{ "dt": "aliases", "dd": "An array of paths to redirect to this folder" },
{ "dt": "[extra]", "dd": "Your extra data block" }
]
}

View File

@ -0,0 +1,22 @@
{
"definitions": [
{ "dt": "content", "dd": "The content of the <kbd>_index.md</kbd> file" },
{ "dt": "title", "dd": "The Title found in the TOML part of <kbd>_index.md</kbd>" },
{ "dt": "description", "dd": "(optional): The description" },
{ "dt": "path", "dd": "As provided by Zola" },
{ "dt": "components", "dd": "The path, split into atoms" },
{ "dt": "permalink", "dd": "The URL for this page" },
{ "dt": "extra", "dd": "The contents of the TOML's <kbd>[extra.*]</kbd> blocks." },
{ "dt": "pages", "dd": "An array of all child pages <em>in this same folder</em>." },
{ "dt": "subsections", "dd": "An array of all child sections <em>in this same folder</em>." },
{
"dt": "toc",
"dd": "An array of <kbd>Header</kbd> objects: id, title, permalink, children (Which is itself an array of <kbd>Header</kbd> objects)"
},
{ "dt": "word_count", "dd": "Unicode-smart." },
{ "dt": "reading_time", "dd": "Number;" },
{ "dt": "assets", "dd": "An array of assets available in this section." },
{ "dt": "ancestors", "dd": "An array of parent paths, with the root being the last one." }
]
}

View File

@ -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);
}

248
sass/index.scss Normal file
View File

@ -0,0 +1,248 @@
@import "design/_normalize";
@import "design/_scales";
@import "design/_typography";
@import "design/fonts/_nunito";
@import "design/fonts/_sauna_mono";
:root {
--default-font-size: var(--step-0);
--background-color: #ffffff;
--border-color: #e9ecef;
--text-color: #1f000d;
--header-size: calc(1.5 * var(--step-0) + 2 * var(--space-3xs));
}
:root {
font-size: var(--default-font-size);
font-family: "Nunito", sans-serif;
}
.toggle-dark {
display: none;
}
html {
height: 100%;
}
body {
height: 100%;
padding-top: var(--header-size);
font-size: var(--default-font-size);
font-family: "Nunito", sans-serif;
font-weight: 400;
}
@mixin fixed-top {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
}
.container {
max-width: 1044px;
width: 100%;
padding: 0 var(--space-xs) 0 var(--space-xs);
margin-right: auto;
margin-left: auto;
}
.hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
margin-left: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.navbar {
display: flex;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: center;
padding-top: 0;
max-height: var(--header-size);
padding-bottom: calc(0.5 * var(--space-3xs));
background-color: var(--background-color);
opacity: 0.95;
border-bottom: 1px solid var(--border-color);
@include fixed-top();
.container {
display: flex;
flex-wrap: inherit;
align-items: center;
justify-content: space-between;
}
.btn {
cursor: pointer;
display: inline-block;
font-weight: 400;
line-height: 1.5;
color: #1d2d35;
text-align: cent er;
vertical-align: middle;
user-select: none;
background-color: transparent;
border: 1px solid transparent;
font-size: 1rem;
border-radius: 0.25rem;
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out,
border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
&:focus {
box-shadow: none;
}
}
.brand {
font-size: var(--step-1);
margin-right: var(--space-s);
font-weight: 700;
color: var(--text-color);
text-decoration: none;
&:visited,
&:hover {
color: var(--text-color);
text-decoration: none;
}
}
form {
font-size: var(--step-0);
flex-grow: 1;
margin-left: var(--space-2xl);
margin-right: var(--space-s);
input {
appearance: none;
background-clip: padding-box;
background: #f8f9fa;
border-radius: 0.25rem;
border: 0;
color: #1d2d35;
line-height: 1.5;
padding: var(--space-3xs) var(--space-l) var(--space-3xs) var(--space-3xs);
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
width: 100%;
}
}
.navbar-nav {
font-size: var(--step-1);
align-items: center;
display: flex;
list-style: none;
gap: var(--space-xs);
margin-bottom: 0;
& > * {
line-height: 1.5;
vertical-align: middle;
}
}
}
@mixin toc-sticky {
max-width: 100%;
height: calc(100vh - var(--header-size));
position: sticky;
z-index: 900;
overflow-y: scroll;
scrollbar-width: thin;
scrollbar-color: #fff #fff;
&:hover {
scrollbar-color: var(--border-color) #fff;
}
.scrollable-sidebar {
scrollbar-width: thin;
display: block;
width: auto;
padding-bottom: 4rem;
ul {
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: 95%;
order: 0;
flex: 0 1 auto;
width: 25%;
padding-right: var(--space-s);
border-right: 1px solid var(--border-color);
}
.docs-toc {
@include toc-sticky();
font-size: 85%;
order: 2;
flex: 0 0 auto;
width: 18.75%;
border-left: 1px solid var(--border-color);
padding-left: var(--space-s);
nav > ul {
& > li:not(:first-child) {
margin-top: var(--space-3xs);
border-top: 1px dashed #e9ecef;
}
& > li:not(:first-child) {
padding-top: var(--space-3xs);
}
ul {
padding-bottom: 0;
}
}
}
article {
order: 1;
padding: var(--space-m) var(--space-s) var(--space-m-l) var(--space-s);
flex: 0 0 auto;
width: 56.25%;
dd {
padding-left: var(--space-s);
}
ul {
padding-left: var(--step-0);
}
pre {
padding: var(--space-2xs);
}
}
}

14
static/main.js Normal file
View File

@ -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');
}

309
static/search.js Normal file
View File

@ -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 href><span></span><span></span></a>";
(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 <b>.
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 <em/> around search terms
if (word[1] === TERM_WEIGHT) {
teaser.push("<b>");
}
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("</b>");
}
}
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));
}

View File

@ -1,26 +1,27 @@
{%- import 'macros/font_preloads.html' as font_preloads -%}
{%- import 'macros/stylesheets.html' as stylesheets -%}
{%- import 'macros/header.html' as header -%}
{%- import 'macros/footer.html' as footer -%}
<!DOCTYPE html>
<html lang="{{ config.extra.language_code | default(value="en-US") }}">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
{{ font_preloads::preloads() }}
{{ stylesheets::stylesheets() }}
{% block styles %}
<link rel="stylesheet" href="{{ get_url(path="index.css") | safe }}">
{% endblock %}
{% block seo %}{% endblock %}
</head>
{% block body %}{% set page_class="home" %}{% endblock body %}
<body class="{{ page_class }}">
{% block header %}
{{ header::header(current_section="/") }}
{% endblock header %}
<body>
{% block body %}
{% include "components/sitebar.html" %}
{% block content %}
{% block header %}
{% endblock header %}
{% block content %}{% endblock content %}
{% block footer %}
{{ footer::footer() }}
{% endblock footer %}
{% block main %}
{% endblock main %}
{% endblock content %}
{% block footer %}
{% endblock footer %}
{% endblock body %}
</body>
</html>

View File

@ -0,0 +1,39 @@
{#
The left sidebar for large document sections navigation for topics that
have child pages. This sidebar contains links to all the pages, and all their
level-one headers, for all the content in the section.
#}
<nav class="left-sidebar" aria-label="Site navigation">
{% set index = get_section(path="_index.md") %}
<h3>{{ index.title }}</h3>
<ul class="notes">
{% for subsection in index.subsections %}
{% set active = "" %}
{% if current_url == subsection.permalink %}
{% set active = " active" %}
{% endif %}
<li>
<h3><a class="note-link {{ active }}" href="{{ subsection.permalink | safe }}">
{{ subsection.title }}
</h3>
{% set pagecount = subsection.pages | length %}
{% if pagecount > 0 %}
<ul>
{% for page in subsection.pages %}
<li><a class="note-link" href="{{ page.permalink | safe }}">{{ page.title }}</a></li>
{% endfor %}
</ul>
{% else %}
<ul>
{% for dest in subsection.toc %}
<li><a class="note-link" href="{{ dest.permalink | safe }}">{{ dest.title }}</a></li>
{% endfor %}
<ul>
{% endif %}
</li>
{% endfor %}
<ul>
</nav>

View File

@ -0,0 +1,42 @@
{#
The left sidebar for singleton pages provides navigation is for topics that
have no child pages. This sidebar contains only links to all the level zero
and level one headers for all content in the site. It should only be used for
document section that have only a section definition file, `_index.md`.
#}
<div class="left-sidebar" aria-label="Site navigation">
<nav class="scrollable-sidebar">
{% set index = get_section(path="_index.md") %}
<h3>{{ index.title }}</h3>
<ul class="notes">
{% for s in index.subsections %}
{% set subsection = get_section(path=s) %}
{% set active = "" %}
{% if current_url == subsection.permalink %}
{% set active = " active" %}
{% endif %}
<li>
<a class="note-link {{ active }}" href="{{ subsection.permalink | safe }}">
{{ subsection.title }}
</a>
{% set pagecount = subsection.pages | length %}
{% if pagecount > 0 %}
<ul>
{% for page in subsection.pages %}
<li><a class="note-link" href="{{ page.permalink | safe }}">{{ page.title }}</a></li>
{% endfor %}
</ul>
{% else %}
<ul>
{% for dest in subsection.toc %}
<li><a class="note-link" href="{{ dest.permalink | safe }}">{{ dest.title }}</a></li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
<ul>
</nav>
</div>

View File

@ -0,0 +1,24 @@
{% if section %} {% set toc = section.toc %} {% else %} {% set toc = page.toc %} {% endif %}
<div class="docs-toc" aria-label="On-page navigation">
<div class="scrollable-sidebar">
<div class="page-links">
<h4>On this page</h3>
<nav id="TableOfContents">
<ul>
{% for h1 in toc %}
<li>
<a href="{{ h1.permalink | safe}}">{{ h1.title }}</a>
{% if h1.children %}
<ul>
{% for h2 in h1.children %}
<li><a href="{{ h2.permalink | safe }}">{{ h2.title }}</a></li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
</nav>
</div>
</div>
</div>

View File

@ -0,0 +1,103 @@
<header class="navbar">
<div class="container">
<label class="menu-icon" for="menu-btn"><span class="navicon"></span></label>
<a class="brand" href="{{ config.base_url | safe }}"
>{{ config.title | default(value="Elf M. Sternberg's Notebook") }}</a
>
<form class="search">
<input
id="userinput"
class="search-form"
type="search"
placeholder="Search notes..."
aria-label="Search notes..."
autocomplete="off"
/>
<div id="suggestions"></div>
</form>
<ul class="navbar-nav">
<li class="nav-item">
<button id="mode" class="btn" type="button" aria-label="Toggle mode">
<span class="toggle-dark"
><svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="feather feather-moon"
>
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg
></span>
<span class="toggle-light"
><svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="feather feather-sun"
>
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg
></span>
</button>
</li>
<li class="nav-item">
<a class="nav-link" href="https://twitter.com/elfsternberg"
><svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="feather feather-twitter"
>
<path
d="M23 3a10.9 10.9 0 0 1-3.14 1.53 4.48 4.48 0 0 0-7.86 3v1A10.66 10.66 0 0 1 3 4s-4 9 5 13a11.64 11.64 0 0 1-7 2c9 5 20 0 20-11.5a4.5 4.5 0 0 0-.08-.83A7.72 7.72 0 0 0 23 3z"
></path></svg
><span class="hidden">Twitter</span></a
>
</li>
<li class="nav-item">
<a class="nav-link" href="https://github.com/elfsternberg"
><svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="feather feather-github"
>
<path
d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"
></path></svg
><span class="hidden">GitHub</span></a
>
</li>
</ul>
</div>
</header>

View File

@ -7,9 +7,7 @@
{% block content %}
<div class="container" role="document">
<div class="main">
<div class="notes-sidebar">
Sidebar!
</div>
{% include "components/left-sidebar-singleton.html" %}
<article>
<h1 class="text-center">{{ section.title }}</h1>
<div class="text-center">{{ section.content | safe }}</div>

View File

@ -1,34 +1,23 @@
{% extends "base.html" %}
{% block content %}
<div class="container" role="document">
<div class="content">
<section class="section container-fluid mt-n3 pb-3">
<div class="row justify-content-center">
<div class="col-lg-12 text-center">
<h1>{{ section.title | default(value="Elf's Notes") }}</h1>
</div>
<article class="text-article">
<p class="lead">
{{ section.extra.lead | default(value="You need to add some content") | safe }}
</p>
</article>
</div>
</section>
</div>
</div>
<section class="section">
<h1>{{ section.title | default(value="Elf's Notes") }}</h1>
<article class="text">
{{ section.content | safe }}
</article>
</section>
<div class="container topic-list">
<h2>Topics Available:</h2>
<section class="section">
<div class="topic-cards">
{% if section.extra.list %} {% for val in section.extra.list %}
<div class="topic-card">
<h3><a href="{{val.url}}">{{ val.title }}</a></h3>
<p>{{ val.content | safe }}</p>
</div>
{% endfor %} {% endif %}
</div>
</section>
</div>
<section class="subject-list">
<h2>Subjects Available:</h2>
<div class="topic-cards">
{% for subsection_path in section.subsections %}
{% set subsection = get_section(path=subsection_path) %}
<div class="topic-card">
<h3><a href="{{subsection.permalink}}">{{ subsection.title }}</a></h3>
<p>{{ subsection.description | default(value="No description provided") }}</p>
</div>
{% endfor %}
</div>
</section>
{% endblock %}

View File

@ -0,0 +1,17 @@
{% macro definition_table(source) %}
<table>
<tbody>
{% for entry in source -%}
<tr>
<td><code>{{ entry.dt | safe }}</code></td>
<td>{{ entry.dd | safe }}</td>
</tr>
{% if entry.definitions -%}
<tr>
<td colspan="2" class="sub-definition">{{ self::definition_table(source=entry.definitions) }}</td>
</tr>
{%- endif %}
{% endfor %}
</tbody>
</table>
{% endmacro %}

View File

@ -2,26 +2,16 @@
{% extends "base.html" %}
{% block body %}
{% if section.extra.class %}
{% set page_class = section.extra.class %}
{% else %}
{% set page_class = "page list" %}
{% endif %}
{% endblock body %}
{% block content %}
<div class="wrap container" role="document">
<div class="container" role="document">
<div class="content">
<div class="row justify-content-center">
<div class="col-md-12 col-lg-10 col-xxl-8">
<article class="text-article">
<div class="page-header">
<h1>{{ section.title }}</h1>
</div>
{{ section.content | safe }}
</article>
</div>
<div class="main">
{% include "components/left-sidebar-singleton.html" %}
{% include "components/right-sidebar.html" %}
<article>
<h1 class="text-center">{{ section.title }}</h1>
<div class="text-center">{{ section.content | safe }}</div>
</article>
</div>
</div>
</div>

View File

@ -0,0 +1,5 @@
{%- import 'macros/definition_table.html' as renderer -%}
<div>
{% set content = load_data(path=source) %}
{{ renderer::definition_table(source=content.definitions) }}
</div>