First deployed version

This commit is contained in:
Ben Edgington
2021-12-07 15:25:24 +00:00
parent feb5df029d
commit 5d4ca465b9
52 changed files with 17622 additions and 1767 deletions

12
.gitignore vendored
View File

@@ -1,3 +1,13 @@
# Deployment utilities
bin/priv/
# Files generated during the build process
src/md/pages/
src/md/annotated.md
index.json
# Junk
node_modules/
.cache/
public
public/
tmp/

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "plugins/gatsby-remark-mathjax3"]
path = plugins/gatsby-remark-mathjax3
url = https://gitlab.com/public-repositories/gatsby-remark-mathjax3.git

111
README.md
View File

@@ -1,54 +1,97 @@
<p align="center">
<a href="https://www.gatsbyjs.com/?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter">
<img alt="Gatsby" src="https://www.gatsbyjs.com/Gatsby-Monogram.svg" width="60" />
</a>
</p>
<h1 align="center">
Gatsby minimal starter
</h1>
# Upgrading Ethereum
## 🚀 Quick start
## Intro
1. **Create a Gatsby site.**
This is my book about Ethereum&nbsp;2.0: Ethereum on proof of stake and beyond.
Use the Gatsby CLI to create a new site, specifying the minimal starter.
You can read it at [upgrading-ethereum.info](https://upgrading-ethereum.info/altair) or at [eth2book.info](https://eth2book.info/altair).
```shell
# create a new Gatsby site using the minimal starter
npm init gatsby
```
It is a work in progress. Currently, the only reasonably complete section is the annotated specification (Part 3). But I thought you might like a glimpse at where it's going. I say more about the plan in the [preface](https://upgrading-ethereum.info/altair/preface).
2. **Start developing.**
I am not looking for contributions. That may change in future, but for now I will not be accepting any PRs to _src/book.md_. Please feel free, however, to raise issues for typos, inaccuracies, omissions, and suggestions. And I'll happily consider PRs for improvements to the CSS or JavaScript.
Navigate into your new sites directory and start it up.
## Licence
```shell
cd my-gatsby-site/
npm run develop
```
The copyright on the original parts of this work belongs to ConsenSys. We are currently deciding what licence to apply.
3. **Open the code and start customizing!**
## Installing
Your site is now running at http://localhost:8000!
### Pre-requisites
Edit `src/pages/index.js` to see your site update in real-time!
Install `node`, `npm`, and `gatsby-cli`. These are my versions:
4. **Learn more**
```
> node --version
v16.11.1
> npm --version
8.1.3
> npm list -g gatsby-cli
/home/ben/.config/nvm/versions/node/v16.11.1/lib
└── gatsby-cli@4.2.0
```
- [Documentation](https://www.gatsbyjs.com/docs/?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter)
`gatsby-cli` can be installed with,
- [Tutorials](https://www.gatsbyjs.com/tutorial/?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter)
```
npm install -g gatsby-cli
```
- [Guides](https://www.gatsbyjs.com/tutorial/?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter)
You'll also need a working `gawk` (GNU awk) installed at _/usr/bin/gawk_ so that the build can preprocess the book document.
- [API Reference](https://www.gatsbyjs.com/docs/api-reference/?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter)
### Building
- [Plugin Library](https://www.gatsbyjs.com/plugins?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter)
Clone this repo. `cd` into it, then:
- [Cheat Sheet](https://www.gatsbyjs.com/docs/cheat-sheet/?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter)
```
git submodule update --init --recursive
npm install
gatsby build
```
## 🚀 Quick start (Gatsby Cloud)
### Viewing
Deploy this starter with one click on [Gatsby Cloud](https://www.gatsbyjs.com/cloud/):
After building as above, do
[<img src="https://www.gatsbyjs.com/deploynow.svg" alt="Deploy to Gatsby Cloud">](https://www.gatsbyjs.com/dashboard/deploynow?url=https://github.com/gatsbyjs/gatsby-starter-minimal)
```
gatsby serve
```
and visit `http://localhost:9000` in a web browser.
Instead of building and serving, you can run `gatsby develop` and point your browser at port 8000. This will not pick up real-time changes to _src/book.md_ and will need to be restarted to pick up changes. It is useful, though, for checking CSS and React changes interactively.
## Editing
The entire text for the book is in the _src/book.md_ file. Everything under _src/md/pages_ is auto-generated and any changes there will be lost.
## How to
### Create a new page
New pages are created by appending HTML comments to headings (first three levels only):
```
## Heading <!-- /new/page/path -->
```
Take care to get the white space correct.
### Make a page unlinkable
Do this if a page has no content yet. It will appear in the index, but not linkable.
Append a `*` to the path:
```
## Heading <!-- /unlinked/page/path* -->
```
### Insert an image
Insert SVG images with
```
<div class="image">
<img src="images/image_0.svg" /><br />
<span>Image caption, centred and italic.</span>
</div>
```

50
bin/build/annotated.awk Executable file
View File

@@ -0,0 +1,50 @@
#!/usr/bin/gawk -f
# Split out the annotated spec chapter as a single page doc and update links
#
# Sends output to stdout
BEGIN {
in_part_3 = 0
print "---"
print "path: /annotated-spec"
print "titles: [\"One Page Annotated Spec\",\"\",\"\"]"
print "index: [999]"
print "---"
}
/<!-- \/part3 -->/ {
in_part_3 = 1
}
/<!-- \/part4 -->/ {
in_part_3 = 0
}
in_part_3 {
# Change the chapter title
if (in_part_3 == 1) {
print "# One Page Annotated Spec"
print ""
print "**Note:** This page is automatically generated from the chapters in [Part 3](/part3). You may find that some internal links are broken."
in_part_3 = 2
next
}
# Rewrite links to images
if ($0 ~ /<img src="md.*"/) {
sub(/src="md\//, "src=\"", $0)
}
# Rewrite urls that are internal to the chapter
gsub(/]\(\/part3\/[^#)]*/, "](", $0)
# Remove page path comments from titles
if ($0 ~ /^#.*<!--/) {
sub(/ <!--.*-->/, "", $0)
}
print
}

96
bin/build/links.awk Executable file
View File

@@ -0,0 +1,96 @@
#!/usr/bin/gawk -f
# Finds internal links that do not point to existing anchors.
#
# Anchors may be
# 1. Full page paths: /part3/introduction
# 2. Headings or <a id="..."></a> elements: #introduction
# 3. A combination: /part3/introduction#introduction
#
# Relative page paths are not supported.
#
# Anchors generated from headings have some rules (imposed by Gatsby):
# - Converted to lower case
# - Spaces become "-"
# - Special characters are omitted: ".,?:'`/[]()" and probably others
# - Underscores and dashes are retained.
#
# Remember to pass the target file twice:
#
# ../bin/build/links.awk book.md book.md
#
BEGIN {
# /contents is OK as a page, but will not be picked up automatically
anchors["/contents"] = 1
# Ditto /annotated-spec
anchors["/annotated-spec"] = 1
}
# Path needs to be set on both passes
/(^# |^## |^### ).* <!-- .* -->$/ {
path = gensub(/^#+ .* <!-- ([^*]+).? -->$/, "\\1", "1")
# We can link to the page directly, without the anchor
if (FNR == NR ) anchors[path] = 1
}
# First pass: build list of anchors
FNR == NR {
# Headings
if ($0 ~ /^#/) {
name = ""
if ($0 ~ / <!-- .* -->$/) {
name = gensub(/^#+ (.*) <!-- .* -->$/, "\\1", "1")
} else {
name = gensub(/^#+ (.*)$/, "\\1", "1")
}
name = tolower(name)
gsub(/ /, "-", name)
gsub(/[^a-z0-9_-]/, "", name)
anchors[path "#" name] = 1
# print path "#" name
}
# Explicit anchors - only one per line allowed, at the start of the line
if ($0 ~ /^<a id=".*"><\/a>$/) {
name = gensub(/^<a id="(.*)"><\/a>$/, "\\1", "1")
anchors[path "#" name] = 1
# print path "#" name
}
next
}
# Second pass: check anchors exist
{
# There may be multiple anchors on the line
# This mess matches non-greedy [foo](#bar) and [foo](/a/b/c#xyz) etc.
while (match($0, /\[[^]]+\]\([#/][^)]+\)/)) {
# Workaround awk's greediness, otherwise we just get the last match per line.
partial = substr($0, RSTART, RLENGTH)
name = gensub(/\[[^]]+\]\(([#/][^)]+)\)/, "\\1", "1", partial)
# Full paths start with "/", paths to the same page with "#". We do not use relative ".." paths.
if (name ~ /^#/) {
anchor = path name
} else {
anchor = name
}
if (anchors[anchor] != 1) {
print("Anchor not found, line " FNR ": " name)
}
$0 = substr($0, RSTART + RLENGTH)
}
}
# Any empty links
/\[[^]]+\]\(\)/ {
print("Empty link, line " FNR)
}

113
bin/build/split.awk Executable file
View File

@@ -0,0 +1,113 @@
#!/usr/bin/gawk -f
# Note: run this via "update.sh" rather than directly.
#
# Split the master markdown file into separate files for each page.
#
# - Creates a directory structure reflecting the path hierarchy of the
# pages.
# - Prepends Gatsby frontmatter to each file.
# - Rewrites links for image files so that the origial file can use
# one path and the split files another.
#
# Rules:
# - New pages can start at markdown heading levels 1, 2, or 3.
# - To start a new page, append " <!-- /path/to/page -->" to the heading.
# - The file containing the page will end up in "./md/pages/path/to/page.md"
# - For the page to be marked "hidden", append a "*" to the path.
# - Images matching /<img src="md.*"/ will have their paths naively rewritten.
BEGIN{
n = 0
filename_prefix = "md/pages"
filename = filename_prefix "/error.md"
h_part = ""
h_chapter = ""
h_part_no = -1 # Number parts from 0
h_chapter_no = 0
h_section_no = 0
}
# Headings with an HTML comment at the end trigger a new page
/^(# |## |### ).* <!-- .* -->$/ {
# Start a new page
if (n > 0) {
close (filename)
}
n++
# Generate frontmatter contents
name = gensub(/^#+ (.*) <!-- .* -->$/, "\\1", "1")
h_path = gensub(/^#+ .* <!-- (.*) -->$/, "\\1", "1")
heading = gensub (/^(#+ .*) <!-- .* -->$/, "\\1", "1")
# Is this page hidden?
if (h_path ~ /\*$/) {
h_path = substr(h_path, 1, length(h_path) - 1)
h_hide = "true"
} else {
h_hide = "false"
}
# Make filesystem path for writing the file
file_path = h_path
sub(/\/[^/]+$/, "", file_path)
system("mkdir -p " filename_prefix file_path " 2>/dev/null")
filename = filename_prefix h_path ".md"
print filename
switch ($0) {
case /^# /:
h_part = name
h_chapter = ""
h_section = ""
h_part_no++
h_chapter_no = 0
idx = h_part_no
break
case /^## /:
h_chapter = name
h_section = ""
h_chapter_no++
h_section_no = 0
idx = h_part_no "," h_chapter_no
break
case /^### /:
h_section = name
h_section_no++
idx = h_part_no "," h_chapter_no "," h_section_no
break
default:
print "Internal error"
exit (1)
}
print "---" > filename
print "hide: " h_hide > filename
print "path: " h_path > filename
print "titles: [\"" h_part "\",\"" h_chapter "\",\"" h_section "\"]" > filename
print "index: [" idx "]" > filename
print "sequence: " n > filename
print "---" > filename
print "\n# " h_part > filename
if (h_chapter != "") print "\n## " h_chapter > filename
if (h_section != "") print "\n### " h_section > filename
next
}
# Rewrite image paths to reflect the directory hierarchy
/<img src="md.*"/ {
prefix = substr(h_path, 2)
gsub(/[^/]*/, "..", prefix)
sub(/src="md/, "src=\"" prefix, $0)
print > filename
next
}
# Pass through everything else as-is
{
print > filename
}

14
bin/build/update.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/bash
# Recreate the individual page markdown files from the master document
# in src/book.md
cd "$(dirname "$0")/../../src"
# Build the markdown pages
rm -rf md/pages/
../bin/build/split.awk book.md
# Build the one page annotated spec
rm -f md/annotated.md
../bin/build/annotated.awk book.md > md/annotated.md

35
bin/util/anchors.awk Executable file
View File

@@ -0,0 +1,35 @@
#!/usr/bin/gawk -f
# Output all eligible anchors in the document
BEGIN {
# /contents is OK as a page, but will not be picked up automatically
print "/contents"
name = ""
}
/(^# |^## |^### ).* <!-- .* -->$/ {
path = gensub(/^#+ .* <!-- ([^*]+).? -->$/, "\\1", "1")
print path
}
# Headings
/^#/ {
if ($0 ~ / <!-- .* -->$/) {
name = gensub(/^#+ (.*) <!-- .* -->$/, "\\1", "1")
} else {
name = gensub(/^#+ (.*)$/, "\\1", "1")
}
name = tolower(name)
gsub(/ /, "-", name)
gsub(/[^a-z0-9_-]/, "", name)
print path "#" name
}
# Explicit anchors - only one per line allowed, at the start of the line
/^<a id=".*"><\/a>$/ {
name = gensub(/^<a id="(.*)"><\/a>$/, "\\1", "1")
print path "#" name
}

26
bin/util/functions.awk Executable file
View File

@@ -0,0 +1,26 @@
#!/usr/bin/gawk -f
# Insert an anchor for each function defined in the document, 2 lines ahead.
NR == 1 {
lm2 = $0
}
NR == 2 {
lm1 = $0
}
NR > 2 {
if ($0 ~ /^def /) {
f = gensub(/^def ([^(]+).+$/, "\\1", "1")
print "\n<a id=\"def_" f "\"></a>"
}
print lm2
lm2 = lm1
lm1 = $0
}
END {
print lm2
print lm1
}

78
bin/util/index.awk Executable file
View File

@@ -0,0 +1,78 @@
#!/usr/bin/gawk -f
# Builds a JSON index from a file of keywords
#
# ./index.awk words.txt ../../src/book.md
#
# Should probably escape quotation marks in page_name and heading
BEGIN {
ORS=""
}
# First pass: build list of things to index
FNR == NR {
refs[$0] = ""
next
}
# Record the current page
/(^# |^## |^### ).* <!-- .* -->$/ {
page_name = gensub(/^#+ (.*) <!-- .* -->$/, "\\1", "1")
page_path = gensub(/^#+ .* <!-- ([^*]+).? -->$/, "\\1", "1")
}
# Record the most recent anchor heading
/^#/ {
heading = ""
if ($0 ~ / <!-- .* -->$/) {
heading = gensub(/^#+ (.*) <!-- .* -->$/, "\\1", "1")
} else {
heading = gensub(/^#+ (.*)$/, "\\1", "1")
}
gsub(/`/, "", heading)
anchor = tolower(heading)
gsub(/ /, "-", anchor)
gsub(/[^a-z0-9_-]/, "", anchor)
for (word in refs) {
done[word] = 0
}
}
# Skipping empty lines gives a nice speed-up
length() {
for (word in refs) {
if (!done[word] && index($0, word)) {
refs[word] = refs[word] "\"" page_path "#" anchor "\","
heads[page_path "#" anchor] = "[\"" page_name "\",\"" heading "\"]"
done[word] = 1
}
}
}
END {
print "{\"refs\":{"
count = length(refs)
for (word in refs) {
count--
references = refs[word]
if (references) {
#remove trailing commas
sub(/,$/, "", references)
print "\"" word "\":[" references "]"
if (count) print ","
}
}
print "},\"heads\":{"
count = length(heads)
for (anchor in heads) {
count--
headings = heads[anchor]
if (headings) {
print "\"" anchor "\":" headings
if (count) print ","
}
}
print "}}"
}

207
bin/util/words.txt Normal file
View File

@@ -0,0 +1,207 @@
`Slot`
`Epoch`
`CommitteeIndex`
`ValidatorIndex`
`Gwei`
`Root`
`Hash32`
`Version`
`DomainType`
`ForkDigest`
`Domain`
`BLSPubkey`
`BLSSignature`
`ParticipationFlags`
`Fork`
`ForkData`
`Checkpoint`
`Validator`
`AttestationData`
`IndexedAttestation`
`PendingAttestation`
`Eth1Data`
`HistoricalBatch`
`DepositMessage`
`DepositData`
`BeaconBlockHeader`
`SigningData`
`ProposerSlashing`
`AttesterSlashing`
`Attestation`
`Deposit`
`VoluntaryExit`
`BeaconBlockBody`
`BeaconBlock`
`BeaconState`
`SignedVoluntaryExit`
`SignedBeaconBlock`
`SignedBeaconBlockHeader`
`SyncAggregate`
`SyncCommittee`
`GENESIS_SLOT`
`GENESIS_EPOCH`
`FAR_FUTURE_EPOCH`
`DEPOSIT_CONTRACT_TREE_DEPTH`
`JUSTIFICATION_BITS_LENGTH`
`PARTICIPATION_FLAG_WEIGHTS`
`ENDIANNESS`
`TIMELY_SOURCE_FLAG_INDEX`
`TIMELY_TARGET_FLAG_INDEX`
`TIMELY_HEAD_FLAG_INDEX`
`TIMELY_SOURCE_WEIGHT`
`TIMELY_TARGET_WEIGHT`
`TIMELY_HEAD_WEIGHT`
`SYNC_REWARD_WEIGHT`
`PROPOSER_WEIGHT`
`WEIGHT_DENOMINATOR`
`BLS_WITHDRAWAL_PREFIX`
`ETH1_ADDRESS_WITHDRAWAL_PREFIX`
`DOMAIN_BEACON_PROPOSER`
`DOMAIN_BEACON_ATTESTER`
`DOMAIN_RANDAO`
`DOMAIN_DEPOSIT`
`DOMAIN_VOLUNTARY_EXIT`
`DOMAIN_SELECTION_PROOF`
`DOMAIN_AGGREGATE_AND_PROOF`
`DOMAIN_SYNC_COMMITTEE`
`DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF`
`DOMAIN_CONTRIBUTION_AND_PROOF`
`G2_POINT_AT_INFINITY`
`MAX_COMMITTEES_PER_SLOT`
`TARGET_COMMITTEE_SIZE`
`MAX_VALIDATORS_PER_COMMITTEE`
`SHUFFLE_ROUND_COUNT`
`HYSTERESIS_QUOTIENT`
`HYSTERESIS_DOWNWARD_MULTIPLIER`
`HYSTERESIS_UPWARD_MULTIPLIER`
`MIN_DEPOSIT_AMOUNT`
`MAX_EFFECTIVE_BALANCE`
`EFFECTIVE_BALANCE_INCREMENT`
`MIN_ATTESTATION_INCLUSION_DELAY`
`SLOTS_PER_EPOCH`
`MIN_SEED_LOOKAHEAD`
`MAX_SEED_LOOKAHEAD`
`MIN_EPOCHS_TO_INACTIVITY_PENALTY`
`EPOCHS_PER_ETH1_VOTING_PERIOD`
`SLOTS_PER_HISTORICAL_ROOT`
`EPOCHS_PER_HISTORICAL_VECTOR`
`EPOCHS_PER_SLASHINGS_VECTOR`
`HISTORICAL_ROOTS_LIMIT`
`VALIDATOR_REGISTRY_LIMIT`
`BASE_REWARD_FACTOR`
`WHISTLEBLOWER_REWARD_QUOTIENT`
`PROPOSER_REWARD_QUOTIENT`
`INACTIVITY_PENALTY_QUOTIENT`
`MIN_SLASHING_PENALTY_QUOTIENT`
`PROPORTIONAL_SLASHING_MULTIPLIER`
`INACTIVITY_PENALTY_QUOTIENT_ALTAIR`
`MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR`
`PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR`
`MAX_PROPOSER_SLASHINGS`
`MAX_ATTESTER_SLASHINGS`
`MAX_ATTESTATIONS`
`MAX_DEPOSITS`
`MAX_VOLUNTARY_EXITS`
`SYNC_COMMITTEE_SIZE`
`EPOCHS_PER_SYNC_COMMITTEE_PERIOD`
`GENESIS_FORK_VERSION`
`GENESIS_DELAY`
`SECONDS_PER_SLOT`
`SECONDS_PER_ETH1_BLOCK`
`MIN_VALIDATOR_WITHDRAWABILITY_DELAY`
`SHARD_COMMITTEE_PERIOD`
`ETH1_FOLLOW_DISTANCE`
`EJECTION_BALANCE`
`MIN_PER_EPOCH_CHURN_LIMIT`
`CHURN_LIMIT_QUOTIENT`
`INACTIVITY_SCORE_BIAS`
`INACTIVITY_SCORE_RECOVERY_RATE`
`integer_squareroot`
`xor`
`bytes_to_uint64`
`eth_aggregate_pubkeys`
`eth_fast_aggregate_verify`
`is_active_validator`
`is_eligible_for_activation_queue`
`is_eligible_for_activation`
`is_slashable_validator`
`is_slashable_attestation_data`
`is_valid_indexed_attestation`
`is_valid_merkle_branch`
`compute_shuffled_index`
`compute_proposer_index`
`compute_committee`
`compute_epoch_at_slot`
`compute_start_slot_at_epoch`
`compute_activation_exit_epoch`
`compute_fork_data_root`
`compute_fork_digest`
`compute_domain`
`compute_signing_root`
`add_flag`
`has_flag`
`get_current_epoch`
`get_previous_epoch`
`get_block_root`
`get_block_root_at_slot`
`get_randao_mix`
`get_active_validator_indices`
`get_validator_churn_limit`
`get_seed`
`get_committee_count_per_slot`
`get_beacon_committee`
`get_beacon_proposer_index`
`get_total_balance`
`get_total_active_balance`
`get_domain`
`get_indexed_attestation`
`get_attesting_indices`
`get_next_sync_committee_indices`
`get_next_sync_committee`
`get_unslashed_participating_indices`
`get_attestation_participation_flag_indices`
`get_flag_index_deltas`
`increase_balance`
`decrease_balance`
`initiate_validator_exit`
`slash_validator`
`state_transition`
`verify_block_signature`
`process_slots`
`process_slot`
`process_epoch`
`process_justification_and_finalization`
`weigh_justification_and_finalization`
`process_inactivity_updates`
`get_base_reward_per_increment`
`get_base_reward`
`get_finality_delay`
`is_in_inactivity_leak`
`get_eligible_validator_indices`
`get_inactivity_penalty_deltas`
`process_rewards_and_penalties`
`process_registry_updates`
`process_slashings`
`process_eth1_data_reset`
`process_effective_balance_updates`
`process_slashings_reset`
`process_randao_mixes_reset`
`process_historical_roots_update`
`process_participation_flag_updates`
`process_sync_committee_updates`
`process_block`
`process_block_header`
`process_randao`
`process_eth1_data`
`process_operations`
`process_proposer_slashing`
`process_attester_slashing`
`process_attestation`
`get_validator_from_deposit`
`process_deposit`
`process_voluntary_exit`
`process_sync_aggregate`
`initialize_beacon_state_from_eth1`
`is_valid_genesis_state`
`translate_participation`
`upgrade_to_altair`

1
gatsby-browser.js Normal file
View File

@@ -0,0 +1 @@
require("prismjs/themes/prism-tomorrow.css")

View File

@@ -1,7 +1,55 @@
module.exports = {
siteMetadata: {
siteUrl: "https://www.yourdomain.tld",
title: "Ethereum 2.0 Explained",
title: "Upgrading Ethereum",
description: `A technical handbook on Ethereum's move to proof of stake and beyond`,
author: `Ben Edgington (@benjaminion)`,
},
plugins: [],
pathPrefix: `/altair`,
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `markdown-pages`,
path: `${__dirname}/src/md`,
},
},
{
resolve: `gatsby-transformer-remark`,
options: {
gfm: true,
plugins: [
`gatsby-remark-autolink-headers`,
'gatsby-remark-numbered-footnotes',
`gatsby-remark-mathjax3`,
{
resolve: "gatsby-remark-external-links",
options: {
target: "_blank",
rel: "external noopener"
}
},
{
resolve: `gatsby-remark-prismjs`,
options: {
noInlineHighlight: true,
},
},
{
resolve: `gatsby-remark-copy-linked-files`,
options: {
destinationDir: `images`,
ignoreFileExtensions: [],
},
},
],
},
},
`gatsby-plugin-catch-links`,
{
resolve: 'gatsby-plugin-htaccess',
options: {
ErrorDocument: `ErrorDocument 404 /altair/404.html`,
},
},
],
};

54
gatsby-node.js Normal file
View File

@@ -0,0 +1,54 @@
const path = require(`path`)
const execSync = require('child_process').execSync;
exports.onPreInit = ({reporter}) => {
reporter.info("Checking internal links...")
try {
const out = execSync('bin/build/links.awk src/book.md src/book.md', {encoding: 'utf8'})
if (out !== "") {
reporter.warn("Found some bad internal links:")
out.split(/\r?\n/).forEach((line, i) => reporter.warn(line))
}
} catch (err) {
reporter.warn("Unable to check internal links:")
err.toString().split(/\r?\n/).forEach((line, i) => reporter.warn(line))
}
reporter.info("Unpacking book source...")
try {
execSync('bin/build/update.sh')
} catch (err) {
reporter.panic("Failed to unpack book source.", err)
}
}
exports.createPages = async ({ actions, graphql }) => {
const { createPage } = actions
const pageTemplate = path.resolve(`src/templates/pageTemplate.js`)
const result = await graphql(`
{
allMarkdownRemark {
edges {
node {
frontmatter {
path
}
}
}
}
}
`)
if (result.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`)
}
result.data.allMarkdownRemark.edges.forEach(({ node }) => {
createPage({
path: node.frontmatter.path,
component: pageTemplate,
})
})
}

12175
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,10 @@
{
"name": "ethereum-2.0-explained",
"name": "upgrading-ethereum-book",
"version": "1.0.0",
"private": true,
"description": "Ethereum 2.0 Explained",
"description": "A technical handbook on Ethereum's move to proof of stake and beyond",
"author": "Ben Edgington",
"keywords": [
"gatsby"
],
"keywords": [],
"scripts": {
"develop": "gatsby develop",
"start": "gatsby develop",
@@ -15,8 +13,23 @@
"clean": "gatsby clean"
},
"dependencies": {
"gatsby": "^3.14.2",
"gatsby": "^4.1.3",
"gatsby-plugin-catch-links": "^4.0.0",
"gatsby-plugin-htaccess": "^1.4.0",
"gatsby-plugin-sharp": "^4.0.0",
"gatsby-remark-autolink-headers": "^5.0.0",
"gatsby-remark-copy-linked-files": "^5.0.0",
"gatsby-remark-external-links": "^0.0.4",
"gatsby-remark-images": "^6.0.0",
"gatsby-remark-numbered-footnotes": "^1.0.1",
"gatsby-remark-prismjs": "^6.1.0",
"gatsby-source-filesystem": "^4.0.0",
"gatsby-transformer-remark": "^5.0.0",
"gatsby-transformer-sharp": "^4.0.0",
"react": "^17.0.1",
"react-dom": "^17.0.1"
"react-dom": "^17.0.1",
"rehype-mathjax": "^3.1.0",
"remark-math": "^4.0.0",
"remark-rehype": "^8.1.0"
}
}

5251
src/book.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
:root {
--sidebar-width: 20vw;
--sidebar-max-width: 400px;
--sidebar-min-width: 240px;
--main-width: 50vw;
--main-max-width: 1200px;
--main-min-width: 800px;
}

View File

@@ -0,0 +1,5 @@
footer {
border-top: solid 1px #999;
padding-top: 1ex;
margin-top: 10ex;
}

17
src/components/footer.js Normal file
View File

@@ -0,0 +1,17 @@
import React from "react"
import "./footer.css"
const Footer = () => {
return (
<footer>
<p>Created by Ben Edgington. © Copyright 2021 ConsenSys. License TBD.</p>
</footer>
)
}
// <footer>
// <p>Created by Ben Edgington. © Copyright 2021 ConsenSys. Licensed under <a href="https://creativecommons.org/licenses/by-sa/4.0/" rel="license noreferrer" target="_blank">CC BY-SA 4.0</a>.</p>
// </footer>
export default Footer

View File

@@ -0,0 +1,33 @@
import React from "react"
import { Link } from "gatsby"
function ConditionalLink({to, children, nolink}) {
const ret = nolink
? <>{children}</>
: <Link to={to} activeClassName="index-active">{children}</Link>
return (ret)
}
export default function NestedList({idx, items, level}) {
var ret = []
var i = idx
while (i < items.length) {
const item = items[i]
const labelSpan = item.label.length === 0 ? <></ > : <span className="label-string">{item.label}</span>
if (item.level === level) {
var foo = ""
if (i + 1 < items.length && items[i + 1].level > level) {
foo = <NestedList key={i + 1} items={items} level={level + 1} idx={i + 1} />
}
ret.push(
<li key={i}><ConditionalLink to={item.link} nolink={item.hide}>{labelSpan} {item.title}</ConditionalLink>{foo}</li>
)
i++
while (i < items.length && items[i].level > level)
i++
} else {
break
}
}
return (<ul>{ret}</ul>)
}

215
src/components/page.css Normal file
View File

@@ -0,0 +1,215 @@
@import "custom.css";
html, body {
margin: 0;
padding: 0;
color: #24292e;
text-rendering: optimizeLegibility;
font-family: system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
}
/* A fix for PgUp/PgDn not working to scroll */
#gatsby-focus-wrapper {
display: contents;
}
main {
display: flex;
flex-direction: row;
width: 100vw;
height: 100vh;
flex-direction: row;
margin: 0px;
padding: 0px;
overflow-y: hidden;
overflow-x: auto;
}
div.main-content {
box-sizing: border-box;
margin: 0px;
margin-left: calc(50vw - min(var(--sidebar-width), var(--sidebar-max-width)) - min(var(--main-max-width), var(--main-width)) / 2);
/* margin-left: calc(50vw - min(var(--sidebar-width), var(--sidebar-max-width)) - min(700px,30vw));*/
width: var(--main-width);
max-width: var(--main-max-width);
min-width: var(--main-min-width);
padding: 0px 64px 0 96px;
overflow-y: auto;
}
code, pre, tt {
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
}
p {
margin-top: 0px;
margin-bottom: 16px;
font-size: 16px;
line-height: 1.5;
}
a {
color: #0366d6;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
a[rel*="external"]::after {
content: "↗";
font-size: 10px;
vertical-align: top;
}
code {
font-size: 95%;
}
h3 {
padding-bottom: 6px;
border-bottom: 1px solid #cccccc;
margin-bottom: 3ex;
}
h4, h5, h6 {
margin-top: 4ex;
margin-bottom: 1ex;
}
h1 + p,
h2 + p,
h3 + p,
h4 + p,
h5 + p,
h6 + p {
margin-top: 1ex;
}
h4 code {font-size: inherit; }
h5 code {
font-size: 110%;
}
div.image {
text-align: center;
padding: 3ex 0;
}
div.image img {
width: 80%;
}
div.image span {
display: inline;
font-style: italic;
}
table {
border: solid #999999 1px;
border-collapse: collapse;
margin: 3ex 0;
}
td, th {
border: solid #999999 1px;
padding: 4px 8px;
text-align: left;
vertical-align: top;
}
th {
color: white;
background-color: #999999;
border: solid #cccccc 1px;
border-top-style: inherit;
}
th:first-child {
border-left-style: inherit;
}
th:last-child {
border-right-style: inherit;
}
th:empty {
display: none;
}
div.section li {
line-height: 1.5;
padding-bottom: 3px;
}
blockquote {
margin-left: 0;
padding-left: 2em;
border-left: solid #ccc 4px;
font-style: italic;
}
/*** Title page format ***/
div.title-page {
display: flex;
margin:6ex 20%;
height: 80vh;
text-align:center;
flex-direction: column;
justify-content: space-around;
}
div.title-page h1, div.title-page h2, div.title-page h3, div.title-page h4 {
border: none;
padding: 0;
margin: 0;
}
div.title-page img {
margin: 0 auto;
}
/*** Code prettifier ***/
pre.language-text {
background-color: inherit;
color: inherit;
}
code.language-text {
color: inherit;
}
div.gatsby-highlight {
margin: 1ex 0 3ex 0;
}
pre[class^="language-"] {
border-radius: 1ex;
}
pre[class^="language-"] code {
font-size: 0.9em;
}
/*** Displyed equations ***/
/* Make larger than the default size */
div.math-display {
font-size: 1.1em;
}
/*** Footnotes ***/
div.footnotes hr {
margin-top: 6ex;
border: 0;
border-top: solid 1px #ccc;
}
div.footnotes {
font-size: 85%;
}

13
src/components/page.js Normal file
View File

@@ -0,0 +1,13 @@
import React from "react"
import "./page.css"
const Layout = ({ children }) => {
return (
<>
<main>{children}</main>
</>
)
}
export default Layout

View File

@@ -0,0 +1,24 @@
import React from "react"
import NestedList from "./nestedlist"
// Format pages as a list according to their index data.
// Depth is the length of prefix to ignore
// We assume the pages are given to us in sorted order
const PageList = ({pages, depth}) => {
const filteredPages = pages.filter(p => p.node.frontmatter.index !== null)
if (filteredPages.length === 0) return null
// Make a flat array of list level and the list info
const layout = filteredPages.map(p => {return ({
level: p.node.frontmatter.index.length,
label: p.node.frontmatter.index.join("."),
title: p.node.frontmatter.titles[p.node.frontmatter.index.length - 1],
link: p.node.frontmatter.path,
hide: p.node.frontmatter.hide === true
})})
return (<NestedList items={layout} level={depth + 1} idx={0} />)
}
export default PageList

View File

@@ -0,0 +1,33 @@
div.page-navi {
box-sizing: border-box;
height: 100%;
padding: 0 1em 0 2em;
margin: 0px;
overflow-y: auto;
color: #999999;
font-size: 80%;
}
div.page-navi a {
color: #777;
}
div.page-navi a:hover {
text-decoration: none;
color: #444;
}
div.page-navi ul {
padding-left: 1em;
}
div.page-navi li {
list-style-type: none;
padding-bottom: 3px;
font-weight: normal;
}
div.page-navi > ul > li {
padding-top: 1ex;
font-weight: bold;
}

View File

@@ -0,0 +1,47 @@
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import NestedList from "./nestedlist"
import "./pagenavi.css"
const PageNavi = ({path}) => {
const data = useStaticQuery(graphql`
{
allMarkdownRemark {
edges {
node {
headings {
value
depth
id
}
frontmatter {
path
}
}
}
}
}
`)
const pages = data.allMarkdownRemark.edges
const headings = pages.filter(page => page.node.frontmatter.path === path)[0].node.headings
if (headings.length === 0) {
return null
}
// console.log(JSON.stringify(thisPage.node.headings, undefined, 2))
const items = headings.filter(h => h.depth >= 2).map(h => {return ({
level: h.depth,
label: "",
title: h.value,
link: "#" + h.id,
hide: false
})})
return (<NestedList items={items} level={2} idx={0} />)
}
export default PageNavi

View File

@@ -0,0 +1,28 @@
div.prevnext {
width: 100%;
margin: 0;
padding: 6px 0;
display: flex;
justify-content: space-between;
}
div.prevnext span {
width: 40%;
display: flex;
}
div.prevnext span.prev {
justify-content: flex-start;
}
div.prevnext span.prev a {
padding-right: 100%;
}
div.prevnext span.next {
justify-content: flex-end;
}
div.prevnext span.next a {
padding-left: 100%;
}

View File

@@ -0,0 +1,58 @@
import React from "react"
import { Link } from "gatsby"
import { useStaticQuery, graphql } from "gatsby"
import "./prevnext.css"
function PrevNextLink(props) {
if (props.page === null || props.page === undefined) return null
const f = props.page.node.frontmatter
var title = f.titles[0]
if (f.titles[1] !== "") title += " > " + f.titles[1]
if (f.titles[2] !== "") title += " > " + f.titles[2]
return(
<Link to={f.path} title={title} rel={props.rel}>{props.children}</Link>
)
}
const PrevNext = (props) => {
const data = useStaticQuery(graphql`
{
allMarkdownRemark {
edges {
node {
frontmatter {
path
titles
sequence
}
}
}
}
}
`)
if (props.seq === null) return null
const pages = data.allMarkdownRemark.edges
// console.log(JSON.stringify(pages, undefined, 2))
const prevPage = pages.filter(p => p.node.frontmatter.sequence === (props.seq - 1))[0]
const nextPage = pages.filter(p => p.node.frontmatter.sequence === (props.seq + 1))[0]
return (
<div className="prevnext">
<span className="prev">
<PrevNextLink page={prevPage} rel="prev">Back</PrevNextLink>
</span>
<span className="next">
<PrevNextLink page={nextPage} rel="next">Next</PrevNextLink>
</span>
</div>
)
}
export default PrevNext

View File

@@ -0,0 +1,59 @@
@import "custom.css";
nav.sidebar {
box-sizing: border-box;
width: var(--sidebar-width);
max-width: var(--sidebar-max-width);
min-width: var(--sidebar-min-width);
height: 100%;
padding: 0 1em 0 2em;
margin: 0px;
overflow-y: auto;
border-right: 1px solid rgb(230, 236, 241);
text-align: center;
background-color: rgb(243, 245, 246);
}
#index {
margin: 0 1em;
text-align: left;
}
#index,
#index a:hover {
text-decoration: none;
}
#index li {
text-indent: -1em;
list-style-type: none;
line-height: 1.4;
font-weight: normal;
}
#index ul {
padding-left: 1em;
}
#index > ul > li {
padding-top: 1ex;
font-weight: bold;
}
#index > ul > li > a > span.label-string {
display: none;
}
#index a.index-active {
text-decoration: underline;
}
nav.sidebar div.sidebar-title {
font-size: 14px;
font-weight: bold;
padding: 10px 0;
}
nav.sidebar div.sidebar-title a:hover {
text-decoration: none;
}

66
src/components/sidebar.js Normal file
View File

@@ -0,0 +1,66 @@
import React from "react"
import { Link } from "gatsby"
import { useStaticQuery, graphql } from "gatsby"
import PageList from "./pagelist"
import "./sidebar.css"
const Sidebar = (props) => {
const data = useStaticQuery(graphql`
{
allMarkdownRemark(
sort: {fields: [frontmatter___sequence]}
filter: {frontmatter: {index: {ne: null}}}
) {
edges {
node {
frontmatter {
hide
path
titles
index
sequence
}
}
}
}
site {
siteMetadata {
title
}
}
}
`)
const pages = data.allMarkdownRemark.edges
// List only parts and chapters and immediate children in the sidebar
const index = props.index !== null ? props.index : []
const filteredPages = index.length < 2
? pages.filter(p => p.node.frontmatter.index.length <= 2)
: pages.filter(p => p.node.frontmatter.index.length <= 2
|| (p.node.frontmatter.index.length === 3
&& p.node.frontmatter.index[0] === index[0]
&& p.node.frontmatter.index[1] === index[1]
)
)
// console.log(JSON.stringify(filteredPages, undefined, 2))
return (
<nav className="sidebar">
<div className="sidebar-title">
<Link
to="/"
>
{data.site.siteMetadata.title}
</Link>
</div>
<div id="index">
<PageList pages={filteredPages} depth={0} />
</div>
</nav>
)
}
export default Sidebar

View File

@@ -0,0 +1,3 @@
.subsection-list .label-string {
display: none;
}

View File

@@ -0,0 +1,50 @@
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import PageList from "./pagelist"
import "./subsections.css"
// Format subsections of the page with index indexArray as a nested list
const Subsections = ({indexArray}) => {
const data = useStaticQuery(graphql`
{
allMarkdownRemark(
sort: {fields: [frontmatter___sequence]}
filter: {frontmatter: {index: {ne: null}}}
) {
edges {
node {
frontmatter {
hide
path
titles
index
sequence
}
}
}
}
}
`)
// Only add the auto index for Parts, not any deeper structure
if (indexArray === null || indexArray.length > 1) return null
// Find pages that are subsections of the page we are on
const pages = data.allMarkdownRemark.edges
const indexFilterString = indexArray.length === 0 ? "" : indexArray.join() + ","
const filteredPages = pages.filter(p => p.node.frontmatter.index.join().startsWith(indexFilterString))
if (filteredPages.length > 0) {
return (
<div className="subsection-list">
<PageList pages={filteredPages} depth={indexArray.length} />
</div>
)
} else {
return null
}
}
export default Subsections

63
src/html.js Normal file
View File

@@ -0,0 +1,63 @@
import React from "react"
import PropTypes from "prop-types"
export default function HTML(props) {
return (
<html lang="en-GB" {...props.htmlAttributes}>
<head>
<meta charSet="utf-8" />
<meta httpEquiv="x-ua-compatible" content="ie=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<title>Upgrading Ethereum</title>
<meta name="twitter:card" content="summary"/>
<meta name="twitter:site" content="eth2book.info"/>
<meta name="twitter:creator" content="@benjaminion_xyz"/>
<meta name="twitter:image" content="https://benjaminion.xyz/f/henohenomoheji.svg"/>
<meta property="og:image" content="https://benjaminion.xyz/f/android-icon-192x192.png"/>
<meta name="description" content="A technical handbook on Ethereum's move to proof of stake and beyond." />
<meta property="og:image" content="https://benjaminion.xyz/android-icon-192x192.png" />
<meta property="og:title" content="Upgrading Ethereum" />
<meta property="og:description" content="A technical handbook on Ethereum's move to proof of stake and beyond." />
<link rel="apple-touch-icon" sizes="57x57" href="https://benjaminion.xyz/f/apple-icon-57x57.png" />
<link rel="apple-touch-icon" sizes="60x60" href="https://benjaminion.xyz/f/apple-icon-60x60.png" />
<link rel="apple-touch-icon" sizes="72x72" href="https://benjaminion.xyz/f/apple-icon-72x72.png" />
<link rel="apple-touch-icon" sizes="76x76" href="https://benjaminion.xyz/f/apple-icon-76x76.png" />
<link rel="apple-touch-icon" sizes="114x114" href="https://benjaminion.xyz/f/apple-icon-114x114.png" />
<link rel="apple-touch-icon" sizes="120x120" href="https://benjaminion.xyz/f/apple-icon-120x120.png" />
<link rel="apple-touch-icon" sizes="144x144" href="https://benjaminion.xyz/f/apple-icon-144x144.png" />
<link rel="apple-touch-icon" sizes="152x152" href="https://benjaminion.xyz/f/apple-icon-152x152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="https://benjaminion.xyz/f/apple-icon-180x180.png" />
<link rel="icon" type="image/png" sizes="192x192" href="https://benjaminion.xyz/f/android-icon-192x192.png" />
<link rel="icon" type="image/png" sizes="32x32" href="https://benjaminion.xyz/f/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="96x96" href="https://benjaminion.xyz/f/favicon-96x96.png" />
<link rel="icon" type="image/png" sizes="16x16" href="https://benjaminion.xyz/f/favicon-16x16.png" />
<link rel="manifest" href="https://benjaminion.xyz/f/manifest.json" />
<meta name="msapplication-TileColor" content="#ffffff" />
<meta name="msapplication-TileImage" content="https://benjaminion.xyz/f/ms-icon-144x144.png" />
<meta name="theme-color" content="#ffffff" />
{props.headComponents}
</head>
<body {...props.bodyAttributes}>
{props.preBodyComponents}
<div
key={`body`}
id="___gatsby"
dangerouslySetInnerHTML={{ __html: props.body }}
/>
{props.postBodyComponents}
</body>
</html>
)
}
HTML.propTypes = {
htmlAttributes: PropTypes.object,
headComponents: PropTypes.array,
bodyAttributes: PropTypes.object,
preBodyComponents: PropTypes.array,
body: PropTypes.string,
postBodyComponents: PropTypes.array,
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

9
src/md/404.md Normal file
View File

@@ -0,0 +1,9 @@
---
path: /404.html
---
# Not Found
Sorry, that page does not exist.
Try the [contents](/contents) page.

9
src/md/contents.md Normal file
View File

@@ -0,0 +1,9 @@
---
path: /contents
titles: ["Contents","",""]
index: [-1]
sequence: 0
---
# Contents

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 112 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 218 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 229 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 226 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 228 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 242 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 40 KiB

12
src/md/title_page.md Normal file
View File

@@ -0,0 +1,12 @@
---
path: /
---
<div class="title-page">
<h1>Upgrading Ethereum</h1>
<h2>A technical handbook on Ethereum's move to proof of stake and beyond</h2>
<h3>Edition 0.1: Altair [WIP]</h3>
<h4>by Ben Edgington</h4>
<img src="images/benjaminion.svg" width="120" alt="" />
<h4><a href="contents">Contents</a></h4>
</div>

View File

@@ -1,54 +0,0 @@
import * as React from "react"
import { Link } from "gatsby"
// styles
const pageStyles = {
color: "#232129",
padding: "96px",
fontFamily: "-apple-system, Roboto, sans-serif, serif",
}
const headingStyles = {
marginTop: 0,
marginBottom: 64,
maxWidth: 320,
}
const paragraphStyles = {
marginBottom: 48,
}
const codeStyles = {
color: "#8A6534",
padding: 4,
backgroundColor: "#FFF4DB",
fontSize: "1.25rem",
borderRadius: 4,
}
// markup
const NotFoundPage = () => {
return (
<main style={pageStyles}>
<title>Not found</title>
<h1 style={headingStyles}>Page not found</h1>
<p style={paragraphStyles}>
Sorry{" "}
<span role="img" aria-label="Pensive emoji">
😔
</span>{" "}
we couldnt find what you were looking for.
<br />
{process.env.NODE_ENV === "development" ? (
<>
<br />
Try creating a page in <code style={codeStyles}>src/pages/</code>.
<br />
</>
) : null}
<br />
<Link to="/">Go home</Link>.
</p>
</main>
)
}
export default NotFoundPage

View File

@@ -1,184 +0,0 @@
import * as React from "react"
// styles
const pageStyles = {
color: "#232129",
padding: 96,
fontFamily: "-apple-system, Roboto, sans-serif, serif",
}
const headingStyles = {
marginTop: 0,
marginBottom: 64,
maxWidth: 320,
}
const headingAccentStyles = {
color: "#663399",
}
const paragraphStyles = {
marginBottom: 48,
}
const codeStyles = {
color: "#8A6534",
padding: 4,
backgroundColor: "#FFF4DB",
fontSize: "1.25rem",
borderRadius: 4,
}
const listStyles = {
marginBottom: 96,
paddingLeft: 0,
}
const listItemStyles = {
fontWeight: 300,
fontSize: 24,
maxWidth: 560,
marginBottom: 30,
}
const linkStyle = {
color: "#8954A8",
fontWeight: "bold",
fontSize: 16,
verticalAlign: "5%",
}
const docLinkStyle = {
...linkStyle,
listStyleType: "none",
marginBottom: 24,
}
const descriptionStyle = {
color: "#232129",
fontSize: 14,
marginTop: 10,
marginBottom: 0,
lineHeight: 1.25,
}
const docLink = {
text: "Documentation",
url: "https://www.gatsbyjs.com/docs/",
color: "#8954A8",
}
const badgeStyle = {
color: "#fff",
backgroundColor: "#088413",
border: "1px solid #088413",
fontSize: 11,
fontWeight: "bold",
letterSpacing: 1,
borderRadius: 4,
padding: "4px 6px",
display: "inline-block",
position: "relative",
top: -2,
marginLeft: 10,
lineHeight: 1,
}
// data
const links = [
{
text: "Tutorial",
url: "https://www.gatsbyjs.com/docs/tutorial/",
description:
"A great place to get started if you're new to web development. Designed to guide you through setting up your first Gatsby site.",
color: "#E95800",
},
{
text: "How to Guides",
url: "https://www.gatsbyjs.com/docs/how-to/",
description:
"Practical step-by-step guides to help you achieve a specific goal. Most useful when you're trying to get something done.",
color: "#1099A8",
},
{
text: "Reference Guides",
url: "https://www.gatsbyjs.com/docs/reference/",
description:
"Nitty-gritty technical descriptions of how Gatsby works. Most useful when you need detailed information about Gatsby's APIs.",
color: "#BC027F",
},
{
text: "Conceptual Guides",
url: "https://www.gatsbyjs.com/docs/conceptual/",
description:
"Big-picture explanations of higher-level Gatsby concepts. Most useful for building understanding of a particular topic.",
color: "#0D96F2",
},
{
text: "Plugin Library",
url: "https://www.gatsbyjs.com/plugins",
description:
"Add functionality and customize your Gatsby site or app with thousands of plugins built by our amazing developer community.",
color: "#8EB814",
},
{
text: "Build and Host",
url: "https://www.gatsbyjs.com/cloud",
badge: true,
description:
"Now youre ready to show the world! Give your Gatsby site superpowers: Build and host on Gatsby Cloud. Get started for free!",
color: "#663399",
},
]
// markup
const IndexPage = () => {
return (
<main style={pageStyles}>
<title>Home Page</title>
<h1 style={headingStyles}>
Congratulations
<br />
<span style={headingAccentStyles}> you just made a Gatsby site! </span>
<span role="img" aria-label="Party popper emojis">
🎉🎉🎉
</span>
</h1>
<p style={paragraphStyles}>
Edit <code style={codeStyles}>src/pages/index.js</code> to see this page
update in real-time.{" "}
<span role="img" aria-label="Sunglasses smiley emoji">
😎
</span>
</p>
<ul style={listStyles}>
<li style={docLinkStyle}>
<a
style={linkStyle}
href={`${docLink.url}?utm_source=starter&utm_medium=start-page&utm_campaign=minimal-starter`}
>
{docLink.text}
</a>
</li>
{links.map(link => (
<li key={link.url} style={{ ...listItemStyles, color: link.color }}>
<span>
<a
style={linkStyle}
href={`${link.url}?utm_source=starter&utm_medium=start-page&utm_campaign=minimal-starter`}
>
{link.text}
</a>
{link.badge && (
<span style={badgeStyle} aria-label="New Badge">
NEW!
</span>
)}
<p style={descriptionStyle}>{link.description}</p>
</span>
</li>
))}
</ul>
<img
alt="Gatsby G Logo"
src="data:image/svg+xml,%3Csvg width='24' height='24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 2a10 10 0 110 20 10 10 0 010-20zm0 2c-3.73 0-6.86 2.55-7.75 6L14 19.75c3.45-.89 6-4.02 6-7.75h-5.25v1.5h3.45a6.37 6.37 0 01-3.89 4.44L6.06 9.69C7 7.31 9.3 5.63 12 5.63c2.13 0 4 1.04 5.18 2.65l1.23-1.06A7.959 7.959 0 0012 4zm-8 8a8 8 0 008 8c.04 0 .09 0-8-8z' fill='%23639'/%3E%3C/svg%3E"
/>
</main>
)
}
export default IndexPage

View File

@@ -0,0 +1,58 @@
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/page"
import Sidebar from "../components/sidebar"
import Subsections from "../components/subsections"
import PrevNext from "../components/prevnext"
import Footer from "../components/footer"
import PageNavi from "../components/pagenavi"
export default function Template({
data,
}) {
const { markdownRemark } = data
const { html } = markdownRemark
//console.log(JSON.stringify(markdownRemark, undefined, 2))
const index_array = markdownRemark.frontmatter.path !== "/contents"
? markdownRemark.frontmatter.index
: []
return (
<Layout>
<Sidebar index={markdownRemark.frontmatter.index} />
<div className="main-content">
<PrevNext seq={markdownRemark.frontmatter.sequence} />
<div className="container">
<div className="section">
<div
className="section-content"
dangerouslySetInnerHTML={{ __html: html }}
/>
<Subsections indexArray={index_array} />
</div>
</div>
<Footer />
<PrevNext seq={markdownRemark.frontmatter.sequence} />
</div>
<div className="page-navi">
<PageNavi path={markdownRemark.frontmatter.path} />
</div>
</Layout>
)
}
export const pageQuery = graphql`
query($path: String!) {
markdownRemark(frontmatter: { path: { eq: $path } }) {
frontmatter {
index
path
sequence
}
html
}
}
`