Skip to content

KasparEtter/ef1p

Repository files navigation

Explained from First Principles

This repository contains the website explained-from-first-principles.com, which is built with Jekyll and published with GitHub Pages. Please read this section before contributing to this project.

Contents

This document has the following sections:

Setup

Requirements

Unix shell

The following instructions assume that you roughly know how to use a Unix shell.

macOS Terminal

If you are using macOS, open the Terminal located at /Applications/Utilities/Terminal.app.

Package manager

The required tools are easiest to install, upgrade and remove with a package manager.

macOS Homebrew

Attention: You might have to perform some of the following steps with a user which has administrator rights at the operating system level, but I still need to verify this.

Homebrew is the most popular package manager for macOS. To check whether Homebrew is already installed on your system, run brew --version. If you have not the newest version, you can update Homebrew with brew update.

If Homebrew is not yet installed, install it by entering in your Terminal:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Git

Git is a distributed version control system designed to track changes in text documents such as source code.

You can run git --version to check whether Git is already installed and download it otherwise.

macOS Git

You can also install Git with Homebrew:

brew install git

Ruby

Install (or upgrade) Ruby.

macOS Ruby

In order to avoid file permission issues, install Ruby with Homebrew on macOS:

brew install ruby

Add the paths indicated by the Ruby install script to your ~/.zshrc:

export PATH="/opt/homebrew/opt/ruby/bin:/opt/homebrew/lib/ruby/gems/3.2.0/bin:$PATH"

Reload your shell for the changes to take effect by opening a new window/tab or by entering . ~/.zshrc.

Depending on what shell you use, your configuration file can be different.

Instructions

Clone this repository

Navigate to the directory in which you would like to store this repository:

cd path/to/desired/directory

Clone this repository (or a fork thereof) either using SSH if you have an account at GitHub with an SSH key:

git clone git@github.com:KasparEtter/ef1p.git

or using HTTPS otherwise:

git clone https://github.com/KasparEtter/ef1p.git

Enter this repository

Navigate to the root directory of this repository:

cd ef1p

Most of the following commands have to be called from this directory.

Update this repository

If you already cloned this repository, you can pull in the newest changes with:

git pull

Install Bundler

Install Bundler and replace a potentially existing installation:

gem install bundler

Install all dependencies

Install all Ruby dependencies with:

bundle config set path 'vendor/bundle'
bundle install

You have to execute the first line only once. The configuration is then stored in .bundle/config. This file is intentionally not under version control.

In order to run the same versions as the GitHub Pages server, update the dependencies from time to time:

bundle update

You can check the versions of your local dependencies with:

gem list

Serve the website

Serve the website from the root directory of this repository:

bundle exec jekyll serve --livereload

If you got no errors, you can open the website at http://localhost:4000.

If you want to serve the website in your local network:

bundle exec jekyll serve --livereload --host=0.0.0.0

Use npm run watch as described below to also watch for changes in scripts and styles.

Development

The articles, scripts and styles need to be rebuilt after making changes to them.

Requirements

Node.js

Install Node.js with npm.

You can check whether Node.js and npm are already installed with:

node -v
npm -v

If they are, you can update npm with:

npm install -g npm
macOS Node.js

You can also install Node.js and npm with Homebrew:

brew install node

Dependencies

Install

Install the npm dependencies as specified in package.json with:

npm install

Update

Update all dependencies to their latest version respecting Semantic Versioning:

npm update

If you want to update all dependencies to their latest version ignoring the specified versions, you can use npm-check-updates as follows:

npm run npm-upgrade
npm install

If vulnerabilities were found, fix them with:

npm audit fix
Exclusions

npm run npm-upgrade ignores upgrades for the following packages:

  • anchor-js: v4.3.0 always shows the anchors in Chrome.
  • bootstrap: Too much effort to migrate to v5.
  • bootswatch: Linked to the version of bootstrap.
  • react and react-dom: React 18 causes the cursor to jump to the end when editing input fields. Clicking on a suggested input value no longer works either. Furthermore, losing focus on an input field no longer triggers a state update in Safari/WebKit.
  • readable-stream: Direct dependency only to avoid bundling issues.

Check

Show the version of a particular direct or indirect dependency:

npm show [dependency] version

Show the tree of all direct and indirect dependencies:

npm list

Combined

All the following scripts are defined in package.json.

Build

Use the following script to build the articles, scripts and styles:

npm run build

Lint

Use the following script to lint the articles, scripts and styles in parallel:

npm run lint

Watch

Use the following script to watch the articles, scripts and styles simultaneously:

npm run watch

Articles

Build

If you change a Markdown file, it needs to be regenerated as HTML and copied to the _site folder:

npm run md-build

Lint

All Markdown files have to pass markdownlint with the rules specified in .markdownlint.json. If you are using Visual Studio Code, you can install this extension.

npm run md-lint

Newlines

Please start a new line at least for each sentence. You can also include more line breaks according to the Semantic Line Breaks Specification.

Watch

If you want the website to reload automatically on any changes to Markdown files, you can run the following script:

npm run md-watch

Please note that all errors by the script md-watch are ignored using 2>/dev/null. If you run into problems, don't forget to remove this suppression for debugging. I decided to do this in order to get rid of the following issues outside of my control:

  • vendor/bundle/ruby/2.7.0/gems/jekyll-3.8.5/lib/jekyll/convertible.rb:41: warning: Using the last argument as keyword parameters is deprecated
  • ERROR: directory is already being watched! for directories in node_modules/puppeteer/ (see this page for more information).

Styles

Build

If you change any of the Sass files in scss, you have to compile, prefix and minify the CSS again with:

npm run scss-build

The full and minified CSS for both the dark and the light theme are written to assets/styles. You have to run npm run md-build to copy the CSS files to the _site directory from which the website is served in order for them to take effect.

Lint

All Sass files have to pass stylelint with the rules specified in .stylelintrc. If you are using Visual Studio Code, you can install this extension.

npm run scss-lint

Watch

If you want to rebuild the styles automatically when you change a Sass file, you can run the following script:

npm run scss-watch

Please note that in order for the rebuilt styles to take effect you also have to run npm run md-watch.

Scripts

Build

If you change any of the TypeScript files in typescript or in one of the article folders, you have to compile and minify them again with:

npm run ts-build

The minified JavaScript is written to assets/scripts. You have to run npm run md-build to copy the JavaScript files to the _site directory from which the website is served in order for them to take effect.

Lint

All TypeScript files have to pass TSLint with the rules specified in tslint.json. If you are using Visual Studio Code, you can install this extension.

npm run ts-lint

Watch

If you want to rebuild the scripts automatically when you change a TypeScript file, you can run the following script:

npm run ts-watch

Please note that in order for the rebuilt scripts to take effect you also have to run npm run md-watch.

Analyze

You can find circular dependencies in the TypeScript code with npm run ts-circular.

If you want to inspect the generated bundles, you can run npm run ts-analyze, which opens a locally hosted website, or npm run ts-stats, which generates the file webpack-stats.json, which can be visualized with this website.

The inspectpack plugin outputs during the build process whether there are any duplicate sources. You can read more about this here.

Libraries

If you want to update the external JavaScript libraries in assets/scripts/external/, which are imported in _layouts/head.html, you can run the following scripts:

npm run fonts-copy
npm run katex-copy
npm run scripts-copy
npm run scripts-download

Favicons

The favicons stored in assets/favicons were generated with RealFaviconGenerator.

You can convert the logo in assets/images from SVG to PNG with:

npm run logo-convert

Documentation

Articles

Articles can have the following variables in their front matter:

  • title: The title of the article as used at the top of the article and in the navigation.
  • category: The name of the category to which the article belongs.
  • author: The author of the article so that this information is included in timestamps of the file.
  • published: The date when the article was first published as YYYY-MM-DD. Omit this variable if the article shall not yet be added to the navigation.
  • modified: The date when the article was last modified as YYYY-MM-DD. Omit this variable if the article has not been modified since its publication.
  • teaser: A short text that shall be used when the article is shared on social media or indexed by search engines.
  • icon: The name of the Font Awesome icon used in the navigation without the fa- prefix.
  • tools: Set this to true if the article has a separate page which includes only the tools.
  • math: Set this to true if you want to activate KaTeX rendering for the article.

Preview

You can refresh the cached preview of an article on Twitter, Facebook, and LinkedIn. In case of Telegram, you need to send the URL to the @webpagebot.

Markdown

Markdown is converted by kramdown according to this syntax specification.

Comments

{::comment}
This text is ignored by kramdown.
{:/comment}
{% comment %}
This text is ignored by Liquid.
{% endcomment %}

Footnotes

This statement requires a source.[^label]

[^label]: This can be written anywhere.

Abbreviations

You can use an abbreviation like HTML anywhere and then provide a definition anywhere.

*[HTML]: Hyper Text Markup Language

Horizontal rules

---

Description list

You can declare a description list like this:

Term
: Description

This is transformed into:

<dl>
  <dt>Term</dt>
  <dd>Description</dd>
</dl>

Table

| Default aligned | Left aligned | Center aligned | Right aligned
|-|:-|:-:|-:
| First body | Second cell | Third cell | Fourth cell
| Second line | a | b | c
| Third line | 1 | 2 | 3
|---
| Second body
| Second line
|===
| Footer row

See Bootstrap for styling options.

HTML blocks

You can use HTML blocks in Markdown. If you want the content of an HTML tag to be processed as Markdown as well, you have to use markdown="1" as an attribute in order to parse its content with the default mechanism. You can also use markdown="block" or markdown="span" if you want the content of the tag to be parsed explicitly as a block or span level element.

Details element

For example, this is how you can declare a collapsed details section with a summary, where the content is still rendered using Markdown.

<details markdown="block" open>
<summary markdown="span" id="appropriate-id">
Summary with *Markdown*.
</summary>

Details with *Markdown*.

</details>

If the information box shall not be open by default, remove the open attribute from the details tag.

Tabbed areas

You can declare tabbed areas as follows after including bindTabbed() in the script of the page:

<div class="tabbed" data-titles="One | Two | Three | All" data-default="All" markdown="block">

One

Two

Three

</div>

If you don't provide the attribute data-default, the first area is displayed by default.

Undesirable line breaks

You can prevent undesirable line breaks by wrapping the text in <span class="text-nowrap" markdown="span">…</span>.

Header IDs

You can provide the header ID yourself if you want:

## Title {#my-id}

Attributes

You can use attribute lists to add attributes to the generated HTML elements:

{:#my-id .first-class .second-class attribute="value"}

Classes are appended to the current value of the class attribute. Attributes provided as name-value pairs, on the other hand, replace previous attributes with the same name.

You can apply the attributes to block elements by putting the list on a separate line directly before or after the block element:

This is a paragraph.
{:.class-of-paragraph}

You can apply the attributes to span elements by putting the list directly after the span element with no space in between:

A [link](test.html){:rel='something'} and some **tools**{:.tools}.

If you want to re-use the same attributes, you can declare an attribute list definition:

{:ref-name: #my-id .my-class}
{:other: ref-name #id-of-other .another-class title="Example"}

Table of contents

## Potentially very long title
{:data-toc-text="Short title"}

## Title omitted from the table of contents
{:data-toc-skip=""}

Images

Simple image

![Name of the image](image.png)

Image with caption

{% include image.md source="source.png" caption="Caption with *Markdown*." %}

The caption string can span several lines. You just need to escape all quotation marks in it. It will be rendered with markdown="span" as a single paragraph.

Image in various sizes

Images in the images subfolder of articles which end on .jpg are automatically scaled to a width of 910, 1820, and 2730 pixels and stored in the subfolder generated when running npm run build, npm run watch, or npm start. If you only want to build or watch these images, you can execute npm run img-build or npm run img-watch instead.

A srcset with the various sizes is generated when you use the include statement.

{% include image.md source="source.jpg" caption="Caption with *Markdown*." %}

This example requires that images/source.jpg exists. The caption is optional and can be skipped.

Dark and light images

If you want to provide a different image for the dark and the light theme, you can add themed="true" to the include statement. Please note that the light image is used for printing and the PDF export.

{% include image.md source="source.png" caption="Caption with *Markdown*." themed="true" %}

This example requires that images/source.dark.png and images/source.light.png exist.

Graphics

Generate SVGs

npm run svg-build builds all SVGs which end with .svg.ts in the graphics subfolder of articles. You can also watch all SVG-related TypeScript files with npm run svg-watch. These scripts are also included in npm run build and npm run watch respectively.

Embed SVG directly

{% include_relative generated/example.embedded.svg %}

Embed SVG with caption

<figure markdown="block">
{% include_relative generated/example.embedded.svg %}
<figcaption markdown="span">
Caption with *Markdown*.
</figcaption>
</figure>

Generate thumbnail

If you want to use an SVG image for the social media preview, you can generate a dark and properly scaled PNG by adding the graphic to the script at code/node/thumbnail.ts. You can then run this script with:

npm run thumbnail

Math

You can write both inline math such as $$f(x) = x^2$$ as well as block math:

$$
e^{i\pi} + 1 = 0
$$

Check out the list of supported functions with tons of examples.

Don't forget to add the following front matter at the beginning of the article:

math: true

If you start an inline math statement on a new line, you have to prefix it with a HTML comment in order to preserve the outline view of Visual Studio Code:

<!-- --> $$1 + 2$$ is a simple equation

PDF

The following command generates a PDF of the given article with Puppeteer:

npm run export <article>

Make sure that you are serving the website (e.g. with npm start) at http://localhost:4000/ before calling the above script. Replace <article> with the name of the article's directory. The script runs headless Chromium to generate the PDF.

Timestamps

Generate, verify, and upgrade timestamps using OpenTimestamps with the following commands:

npm run ots-remove <article>
npm run ots-stamp <article>
npm run ots-info <article>
npm run ots-verify <article>
npm run ots-upgrade <article>
npm run ots-remove-bak <article>

Make sure that the PDF has already been generated before calling the second command.

About

Contributions

Please open an issue if you found a typographical error, a factual inaccuracy or a logical fallacy. Please also let me know if an explanation or argument is difficult to follow. In general, I prefer issues to pull requests. By creating a pull request, you transfer the copyright of your contribution without any limitations to me.

Dependencies

The most important third party projects used by this website are:

More dependencies are listed in package.json and Gemfile.

Copyright

The copyright for the content of this repository, excluding the aforementioned dependencies, belongs to Kaspar Etter.

License

The content of this repository, excluding the aforementioned dependencies, is licensed under the Creative Commons Attribution 4.0 International License.

Contact

Please do not hesitate to contact me.

About

Explained from First Principles is a technology, science, and philosophy blog for curious people who want to understand and change the world.

Topics

Resources

License

Stars

Watchers

Forks