Compare commits
5 Commits
5517b77fad
...
361f541a84
Author | SHA1 | Date |
---|---|---|
Elf M. Sternberg | 361f541a84 | |
Elf M. Sternberg | d079e33c04 | |
Elf M. Sternberg | 3a499aa5dd | |
Elf M. Sternberg | bfb15b0f19 | |
Elf M. Sternberg | 58cf0c9cbd |
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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") }}
|
||||
```
|
||||
|
|
@ -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
|
||||
|
|
@ -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" }
|
||||
]
|
||||
}
|
|
@ -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." }
|
||||
]
|
||||
}
|
|
@ -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" }
|
||||
]
|
||||
}
|
|
@ -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." }
|
||||
]
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
Loading…
Reference in New Issue