├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.yml
├── .gitignore
├── .gitmodules
├── .node-version
├── .prettierignore
├── .vscode
└── launch.json
├── LICENSE
├── README.md
├── gatsby-browser.js
├── gatsby-config.js
├── gatsby-node.js
├── lint-staged.config.js
├── netlify.toml
├── package.json
├── prettier.config.js
├── src
├── components
│ ├── audio.js
│ ├── avatar.js
│ ├── blog
│ │ ├── blog-index.js
│ │ ├── blog-layout.js
│ │ ├── blog-list.js
│ │ ├── blog-tag.js
│ │ └── blog-tags.js
│ ├── book-card.js
│ ├── featured.js
│ ├── global.css
│ ├── latest-articles.js
│ ├── layout
│ │ ├── TextStyles.js
│ │ ├── figure.js
│ │ ├── footer.js
│ │ ├── header-links.js
│ │ ├── header.js
│ │ ├── layout.js
│ │ ├── links.js
│ │ ├── linktip-preview.js
│ │ ├── linktip.js
│ │ ├── logo.js
│ │ ├── mdx-components-with-previews.js
│ │ ├── mdx-components.js
│ │ ├── sidenote.js
│ │ ├── tippyBox.css
│ │ └── tooltip.js
│ ├── notes
│ │ ├── note-nav.js
│ │ └── note.js
│ ├── poems
│ │ ├── poem-layout.js
│ │ ├── poem-list.js
│ │ ├── poem-tag.js
│ │ └── poem-tags.js
│ ├── posts
│ │ ├── post-link.js
│ │ ├── post-list.js
│ │ ├── post-tag.js
│ │ ├── post-tags.js
│ │ └── post.js
│ ├── prism.css
│ ├── seo.js
│ ├── similar-content.js
│ └── tags
│ │ ├── list-item.js
│ │ ├── list.js
│ │ ├── tag.js
│ │ └── tags.js
├── content
│ └── canon.yml
├── favicon.png
├── lib
│ ├── breakpoints.js
│ ├── colors.js
│ └── dendron-parse-url.js
└── pages
│ ├── about.mdx
│ ├── articles
│ ├── all.js
│ └── index.mdx
│ ├── blog
│ └── index.mdx
│ ├── canon.js
│ ├── index.mdx
│ ├── mileage-metrics.mdx
│ ├── notes
│ ├── 404.mdx
│ └── index.js
│ ├── poetry
│ ├── all.js
│ ├── index.mdx
│ └── seasons-of-thought.mdx
│ └── privacy.mdx
├── static
├── 2021-06-03-07-35-43.png
├── 2021-06-05-14-49-38.png
├── 9a-XUSP.png
├── What-is-a-digital-garden.png
├── _redirects
├── create-github-project.png
├── create-wordpress-instance.png
├── dendron-frontmatter.png
├── evening-praise.mp3
├── evergreen.mp3
├── how-colorsdanielchapmandev-gets-its-content.png
├── little-women-pages.png
├── my-github-learning-project.png
├── org-roam-graph.png
├── to-the-green-rock.mp3
└── wordpress-instance-setup.png
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["@emotion"],
3 | "presets": [["babel-preset-gatsby"]]
4 | }
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_size = 2
7 | indent_style = space
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md*]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | .cache
2 |
3 | dist
4 | public
5 | node_modules
6 | package.json
7 | *.md
8 |
--------------------------------------------------------------------------------
/.eslintrc.yml:
--------------------------------------------------------------------------------
1 | root: true
2 |
3 | parserOptions:
4 | ecmaVersion: 10
5 | sourceType: module
6 | ecmaFeatures:
7 | jsx: true
8 |
9 | env:
10 | es6: true
11 | node: true
12 |
13 | plugins:
14 | - import
15 | - prettier
16 | - '@emotion'
17 |
18 | extends:
19 | - plugin:react/recommended
20 | - plugin:import/errors
21 | - prettier
22 |
23 | rules:
24 | eqeqeq: 0
25 | no-console: 0
26 | no-eq-null: 0
27 | no-negated-condition: 0
28 | no-unused-vars:
29 | - error
30 | - args: after-used
31 | argsIgnorePattern: '^_'
32 | no-var: error
33 | import/no-extraneous-dependencies: error
34 | prettier/prettier: error
35 | react/prop-types: off
36 | react/display-name: off
37 | react/no-children-prop: off
38 | react/react-in-jsx-scope: off
39 | '@emotion/pkg-renaming': error
40 |
41 | settings:
42 | react:
43 | version: 16.10.1
44 |
45 | overrides:
46 | - files: '*.md'
47 | extends: plugin:mdx/recommended
48 |
49 | - files: '*.mdx'
50 | extends:
51 | - plugin:mdx/recommended
52 | - plugin:mdx/overrides
53 | rules:
54 | import/no-extraneous-dependencies: 0
55 |
56 | - files:
57 | - '**/test/**/*.js'
58 | - '**/*.test.js'
59 | env:
60 | jest: true
61 |
62 | - files:
63 | - 'src/**/*js'
64 | env:
65 | browser: true
66 | rules:
67 | import/default: 0
68 | import/namespace: 0
69 | import/named: 0
70 | import/no-default-export: 0
71 | import/no-extraneous-dependencies: off
72 | import/no-unresolved: off
73 | no-unused-vars: 0
74 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | .cache/
61 | public
62 | yarn-error.log
63 |
64 | \.vscode/settings\.json
65 |
66 | \.DS_Store
67 | content/notes/org-roam.db
68 | content/notes/.dir-locals.el
69 | content/notes/.obsidian/cache
70 | content/notes/.obsidian/config
71 | content/notes/.obsidian/workspace
72 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "My-notes"]
2 | path = My-notes
3 | url = git@github.com:dschapman/My-notes
4 | [submodule "dschapman-com-content"]
5 | path = dschapman-com-content
6 | url = git@github.com:dschapman/dschapman-com-content
7 |
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | 18
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .cache
2 |
3 | dist
4 | public
5 | coverage
6 | yarn.lock
7 | node_modules
8 | package-lock.json
9 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dschapman/dschapman-com/7b8c29a7f8afc3c7672b836460404aa34b35b932/.vscode/launch.json
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Daniel Chapman (D.S. Chapman).
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # About this Website
2 |
3 | The website currently has three different types of posts - articles, poems, and notes. All three are implemented with gatsby-plugin-mdx and I curate links on /articles and /poetry respectively. On the backend I write my notes using [Dendron](https://dendron.so)
4 |
5 | ## 📝 Notes
6 |
7 | Dendron notes use wikilinks, are hierarchical, and context aware. Currently I use gatsby-remark-double-brackets-link to handle the wikilinks and gatsby-remark-copy-linked-files to display when these notes are referenced by another note.
8 |
9 | ## 📘 Articles
10 |
11 | Implemented in gatsby-plugin-mdx. The slug is drawn from slug in the frontmatter, title, and excerpt are also important values. A list of all tags used in articles can be viewed at /articles/tags. These tags will also list links to any note pages that are tagged as well.
12 |
13 | ## 📜 Poems
14 |
15 | Like Articles, all the poems on the site can be seen at /poetry/all. Much of the frontmatter for articles and poems are the same, the exception is the `recording` element which takes the location of an MP3 file and will generate a player at the top of the poem if one exists. Poems have the same tagging system as Articles.
16 |
17 | ## 📄 Pages
18 |
19 | Make sure to import a layout if you're creating an .mdx page in src/pages
20 |
21 | ## 📁 Directory Structure
22 |
23 | ```
24 | .
25 | ├── LICENSE
26 | ├── README.md
27 | ├── content
28 | │ ├── assets
29 | │ ├── notes <-- Note .mdx or .md files go here
30 | │ ├── poems <-- Poem .mdx or .md files go here
31 | │ │ └── seasons-of-thought
32 | │ └── posts <-- Article .mdx or .md files go here
33 | ├── src
34 | │ ├── @aengusm / gatsby-theme-brain
35 | │ ├── components
36 | │ │ ├── layout <-- General Layout
37 | │ │ ├── notes <-- Modified layout for the notes
38 | │ │ ├── poems <-- Modified layout for poems
39 | │ │ ├── posts <-- Modified layout for articles
40 | │ │ └── tags <-- information on generating tags
41 | │ └── pages <-- Pages (often .mdx) go here
42 | ├── static <-- It might not be optimized but its sometimes easier to link to images stored here.
43 | ├── gatsby-config.js
44 | ├── gatsby-node.js
45 | └── yarn.lock
46 | ```
47 |
48 | ## ↔️ Bidirectional Links / Link Previews
49 |
50 | Link Previews are implemented using Tippy JS Tooltips. gatsby-theme-brain provides native bidirectional linking and the information to easily implement link previews (with a slightly modified GraphQL query); however, in order to get this working across the site I added an MDX component that cycles through every single mdx page and puts the childMdx.body value inside of a tooltip if the slugs match.
51 |
52 | Link Previews only show up on larger screen size so make sure to check out the website on your desktop.
53 |
54 | ## 🕺🏼 Styling
55 |
56 | This site uses emotion for CSS in JS styling as well as some vanilla CSS. The CSS is inspired and adapted from [Tufte's CSS](https://github.com/edwardtufte/tufte-css).
57 |
--------------------------------------------------------------------------------
/gatsby-browser.js:
--------------------------------------------------------------------------------
1 | require('./src/components/prism.css')
2 |
--------------------------------------------------------------------------------
/gatsby-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | siteMetadata: {
3 | siteUrl: 'https://www.dschapman.com',
4 | title: 'D.S. Chapman',
5 | seoTitleAddition1: 'Website and Digital Home',
6 | seoTitleAddition2: 'Articles, Poetry, & Musings',
7 | twitter: '@ds_chapman',
8 | description:
9 | 'The website and digital home of writer and poet D.S. Chapman. Explore for articles, poetry, and other projects including my recent book - Seasons of Thought.',
10 | },
11 | plugins: [
12 | 'gatsby-plugin-sharp',
13 | 'gatsby-remark-images',
14 | 'gatsby-plugin-react-helmet',
15 | {
16 | resolve: `gatsby-plugin-mdx`,
17 | options: {
18 | extensions: [`.mdx`, `.md`],
19 | gatsbyRemarkPlugins: [
20 | {
21 | resolve: `gatsby-remark-images`,
22 | options: {
23 | // should this be configurable by the end-user?
24 | maxWidth: 1380,
25 | },
26 | },
27 | { resolve: `gatsby-remark-copy-linked-files` },
28 | { resolve: `gatsby-remark-smartypants` },
29 | { resolve: `gatsby-remark-slug` },
30 | { resolve: `gatsby-remark-mermaid` },
31 | {
32 | resolve: `gatsby-remark-double-brackets-link`,
33 | options: {
34 | titleToURLPath: `${__dirname}/src/lib/dendron-parse-url.js`,
35 | },
36 | },
37 | {
38 | resolve: `gatsby-remark-prismjs`,
39 | options: {
40 | // Class prefix for
tags containing syntax highlighting;
41 | // defaults to 'language-' (e.g. ).
42 | // If your site loads Prism into the browser at runtime,
43 | // (e.g. for use with libraries like react-live),
44 | // you may use this to prevent Prism from re-processing syntax.
45 | // This is an uncommon use-case though;
46 | // If you're unsure, it's best to use the default value.
47 | classPrefix: 'language-',
48 | // This is used to allow setting a language for inline code
49 | // (i.e. single backticks) by creating a separator.
50 | // This separator is a string and will do no white-space
51 | // stripping.
52 | // A suggested value for English speakers is the non-ascii
53 | // character '›'.
54 | inlineCodeMarker: null,
55 | // This lets you set up language aliases. For example,
56 | // setting this to '{ sh: "bash" }' will let you use
57 | // the language "sh" which will highlight using the
58 | // bash highlighter.
59 | aliases: {},
60 | // This toggles the display of line numbers globally alongside the code.
61 | // To use it, add the following line in gatsby-browser.js
62 | // right after importing the prism color scheme:
63 | // require("prismjs/plugins/line-numbers/prism-line-numbers.css")
64 | // Defaults to false.
65 | // If you wish to only show line numbers on certain code blocks,
66 | // leave false and use the {numberLines: true} syntax below
67 | showLineNumbers: false,
68 | // If setting this to true, the parser won't handle and highlight inline
69 | // code used in markdown i.e. single backtick code like `this`.
70 | noInlineHighlight: false,
71 | // This adds a new language definition to Prism or extend an already
72 | // existing language definition. More details on this option can be
73 | // found under the header "Add new language definition or extend an
74 | // existing language" below.
75 | languageExtensions: [
76 | {
77 | language: 'superscript',
78 | extend: 'javascript',
79 | definition: {
80 | superscript_types: /(SuperType)/,
81 | },
82 | insertBefore: {
83 | function: {
84 | superscript_keywords: /(superif|superelse)/,
85 | },
86 | },
87 | },
88 | ],
89 | // Customize the prompt used in shell output
90 | // Values below are default
91 | prompt: {
92 | user: 'root',
93 | host: 'localhost',
94 | global: false,
95 | },
96 | // By default the HTML entities <>&'" are escaped.
97 | // Add additional HTML escapes by providing a mapping
98 | // of HTML entities and their escape value IE: { '}': '{' }
99 | escapeEntities: {},
100 | },
101 | },
102 | ],
103 | },
104 | },
105 | {
106 | resolve: 'gatsby-source-filesystem',
107 | options: {
108 | name: 'assets',
109 | path: `${__dirname}/dschapman-com-content/assets/`,
110 | },
111 | },
112 | {
113 | resolve: 'gatsby-source-filesystem',
114 | options: {
115 | name: 'articles',
116 | path: `${__dirname}/dschapman-com-content/posts/`,
117 | },
118 | },
119 | {
120 | resolve: 'gatsby-source-filesystem',
121 | options: {
122 | name: 'blogs',
123 | path: `${__dirname}/dschapman-com-content/blog/`,
124 | },
125 | },
126 | {
127 | resolve: `gatsby-plugin-google-analytics`,
128 | options: {
129 | trackingId: 'UA-87782104-2',
130 | // Puts tracking script in the head instead of the body
131 | head: true,
132 | // Setting this parameter is optional
133 | },
134 | },
135 | {
136 | resolve: 'gatsby-source-filesystem',
137 | options: {
138 | name: 'poetry',
139 | path: `${__dirname}/dschapman-com-content/poems/`,
140 | },
141 | },
142 | {
143 | resolve: 'gatsby-source-filesystem',
144 | options: {
145 | name: 'pages',
146 | path: `${__dirname}/src/pages/`,
147 | },
148 | },
149 | {
150 | resolve: 'gatsby-source-filesystem',
151 | options: {
152 | name: 'My-notes',
153 | path: `${__dirname}/My-notes/`,
154 | },
155 | },
156 |
157 | 'gatsby-plugin-sitemap',
158 | 'gatsby-plugin-twitter',
159 | 'gatsby-plugin-netlify',
160 | {
161 | resolve: `gatsby-transformer-markdown-references`,
162 | options: {
163 | types: ['Mdx'], // or ["MarkdownRemark"] (or both)
164 | },
165 | },
166 | {
167 | resolve: `gatsby-plugin-feed`,
168 | options: {
169 | query: `
170 | {
171 | site {
172 | siteMetadata {
173 | title
174 | description
175 | siteUrl
176 | site_url: siteUrl
177 | }
178 | }
179 | }
180 | `,
181 | feeds: [
182 | {
183 | serialize: ({ query: { site, allMdx } }) => {
184 | {
185 | return allMdx.edges.map((edge) => {
186 | if (edge.node.frontmatter.slug != null) {
187 | return Object.assign({}, edge.node.frontmatter, {
188 | title: edge.node.frontmatter.title,
189 | description: edge.node.frontmatter.excerpt,
190 | date: edge.node.frontmatter.date,
191 | url:
192 | site.siteMetadata.siteUrl + edge.node.frontmatter.slug,
193 | guid:
194 | site.siteMetadata.siteUrl + edge.node.frontmatter.slug,
195 | custom_elements: [
196 | {
197 | 'content:encoded': edge.node.html
198 | .replace(
199 | /(?<=\"|\s)\/static\//g,
200 | `${site.siteMetadata.siteUrl}\/static\/`
201 | )
202 | .replace(/\[\[(\w*)\|(\w*)\]\]/g, '$2'),
203 | },
204 | ],
205 | })
206 | } else {
207 | let year = edge.node.frontmatter.date.substring(0, 4)
208 | let month = edge.node.frontmatter.date.substring(5, 7)
209 | return Object.assign({}, edge.node.frontmatter, {
210 | title: edge.node.frontmatter.title,
211 | description: edge.node.excerpt,
212 | date: edge.node.frontmatter.date,
213 | url:
214 | site.siteMetadata.siteUrl +
215 | `/blog/${year}/${month}/${edge.node.slug}`,
216 | guid:
217 | site.siteMetadata.siteUrl +
218 | `/blog/${year}/${month}/${edge.node.slug}`,
219 | custom_elements: [
220 | {
221 | 'content:encoded': edge.node.html
222 | .replace(
223 | /(?<=\"|\s)\/static\//g,
224 | `${site.siteMetadata.siteUrl}\/static\/`
225 | )
226 | .replace(/\[\[(\w*)\|(\w*)\]\]/g, '$2'),
227 | },
228 | ],
229 | })
230 | }
231 | })
232 | }
233 | },
234 | query: `
235 | {
236 | allMdx(
237 | filter: {fileAbsolutePath: {regex: "/dschapman-com-content/(?:posts|blog)/"}}
238 | sort: {order: DESC, fields: frontmatter___date}
239 | ) {
240 | edges {
241 | node {
242 | frontmatter {
243 | slug
244 | title
245 | date
246 | excerpt
247 | }
248 | html
249 | slug
250 | excerpt
251 | }
252 | }
253 | }
254 | }
255 |
256 | `,
257 | output: '/rss.xml',
258 | title: "D.S. Chapman's RSS Feed",
259 | },
260 | ],
261 | },
262 | },
263 | ],
264 | }
265 |
--------------------------------------------------------------------------------
/gatsby-node.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | exports.createPages = async ({ graphql, actions, reporter }) => {
4 | // Destructure the createPage function from the actions object
5 | const { createPage } = actions
6 |
7 | const result = await graphql(`
8 | query {
9 | dendron: allMdx(
10 | filter: {
11 | fileAbsolutePath: { regex: "/My-notes/" }
12 | frontmatter: { published: { eq: true } }
13 | }
14 | ) {
15 | edges {
16 | node {
17 | frontmatter {
18 | id
19 | title
20 | }
21 | id
22 | }
23 | previous {
24 | id
25 | }
26 | next {
27 | id
28 | }
29 | }
30 | }
31 | poems: allMdx(
32 | filter: { fileAbsolutePath: { regex: "/dschapman-com-content/poems/" } }
33 | ) {
34 | edges {
35 | node {
36 | frontmatter {
37 | slug
38 | tags
39 | }
40 | id
41 | }
42 | previous {
43 | id
44 | }
45 | next {
46 | id
47 | }
48 | }
49 | }
50 | articles: allMdx(
51 | filter: { fileAbsolutePath: { regex: "/dschapman-com-content/posts/" } }
52 | ) {
53 | edges {
54 | node {
55 | frontmatter {
56 | slug
57 | tags
58 | }
59 | id
60 | }
61 | previous {
62 | id
63 | }
64 | next {
65 | id
66 | }
67 | }
68 | }
69 | blogs: allMdx(
70 | filter: { fileAbsolutePath: { regex: "/dschapman-com-content/blog/" } }
71 | ) {
72 | edges {
73 | node {
74 | frontmatter {
75 | title
76 | date
77 | tags
78 | }
79 | id
80 | slug
81 | }
82 | previous {
83 | id
84 | }
85 | next {
86 | id
87 | }
88 | }
89 | }
90 | articleTagsGroup: allMdx(
91 | filter: { fileAbsolutePath: { regex: "/dschapman-com-content/posts/" } }
92 | ) {
93 | group(field: frontmatter___tags) {
94 | fieldValue
95 | totalCount
96 | }
97 | }
98 | blogTagsGroup: allMdx(
99 | filter: { fileAbsolutePath: { regex: "/dschapman-com-content/blog/" } }
100 | ) {
101 | group(field: frontmatter___tags) {
102 | fieldValue
103 | totalCount
104 | }
105 | }
106 | poemTagsGroup: allMdx(
107 | filter: { fileAbsolutePath: { regex: "/dschapman-com-content/poems/" } }
108 | ) {
109 | group(field: frontmatter___tags) {
110 | fieldValue
111 | totalCount
112 | }
113 | }
114 | allTagsGroup: allMdx {
115 | group(field: frontmatter___tags) {
116 | fieldValue
117 | totalCount
118 | }
119 | }
120 | }
121 | `)
122 |
123 | if (result.errors) {
124 | reporter.panicOnBuild('🚨 ERROR: Loading "createPages" query')
125 | }
126 | // Create blog post pages.
127 | const poems = result.data.poems.edges
128 | const dendron = result.data.dendron.edges
129 | const articles = result.data.articles.edges
130 | const blogs = result.data.blogs.edges
131 | const articleTags = result.data.articleTagsGroup.group
132 | const poemTags = result.data.poemTagsGroup.group
133 | const blogTags = result.data.blogTagsGroup.group
134 | const allTags = result.data.allTagsGroup.group
135 | // you'll call `createPage` for each result
136 | dendron.forEach(({ node }) => {
137 | createPage({
138 | path: `notes/${node.frontmatter.id}`,
139 | component: path.resolve(`./src/components/notes/note.js`),
140 | context: { id: node.id },
141 | })
142 | })
143 | poems.forEach(({ node }) => {
144 | createPage({
145 | // This is the slug you created before
146 | // (or `node.frontmatter.slug`)
147 | path: node.frontmatter.slug,
148 | // This component will wrap our MDX content
149 | component: path.resolve(`./src/components/poems/poem-layout.js`),
150 | // You can use the values in this context in
151 | // our page layout component
152 | context: { id: node.id },
153 | })
154 | })
155 | blogs.forEach(({ node }) => {
156 | let year = node.frontmatter.date.substring(0, 4)
157 | let month = node.frontmatter.date.substring(5, 7)
158 | createPage({
159 | path: `blog/${year}/${month}/${node.slug}`,
160 | component: path.resolve(`./src/components/blog/blog-layout.js`),
161 | context: { id: node.id },
162 | })
163 | })
164 | articles.forEach(({ node }) => {
165 | createPage({
166 | // This is the slug you created before
167 | // (or `node.frontmatter.slug`)
168 | path: node.frontmatter.slug,
169 | // This component will wrap our MDX content
170 | component: path.resolve(`./src/components/posts/post.js`),
171 | // You can use the values in this context in
172 | // our page layout component
173 | context: { id: node.id },
174 | })
175 | })
176 | articleTags.forEach((articleTag) => {
177 | let tag = articleTag.fieldValue
178 | let slug = tag
179 | .toString()
180 | .toLowerCase()
181 | .replace(/\s+/g, '-') // Replace spaces with -
182 | .replace(/[^\w\-]+/g, '') // Remove all non-word chars
183 | .replace(/\-\-+/g, '-') // Replace multiple - with single -
184 | .replace(/^-+/, '') // Trim - from start of text
185 | .replace(/-+$/, '') // Trim - from end of text
186 |
187 | createPage({
188 | path: `articles/tag/${slug}`,
189 | component: path.resolve(`./src/components/posts/post-tag.js`),
190 | context: { tag: tag },
191 | })
192 | })
193 | blogTags.forEach((blogTag) => {
194 | let tag = blogTag.fieldValue
195 | let slug = tag
196 | .toString()
197 | .toLowerCase()
198 | .replace(/\s+/g, '-') // Replace spaces with -
199 | .replace(/[^\w\-]+/g, '') // Remove all non-word chars
200 | .replace(/\-\-+/g, '-') // Replace multiple - with single -
201 | .replace(/^-+/, '') // Trim - from start of text
202 | .replace(/-+$/, '') // Trim - from end of text
203 |
204 | createPage({
205 | path: `blog/tag/${slug}`,
206 | component: path.resolve(`./src/components/blog/blog-tag.js`),
207 | context: { tag: tag },
208 | })
209 | })
210 | poemTags.forEach((poemTag) => {
211 | let tag = poemTag.fieldValue
212 | let slug = tag
213 | .toString()
214 | .toLowerCase()
215 | .replace(/\s+/g, '-') // Replace spaces with -
216 | .replace(/[^\w\-]+/g, '') // Remove all non-word chars
217 | .replace(/\-\-+/g, '-') // Replace multiple - with single -
218 | .replace(/^-+/, '') // Trim - from start of text
219 | .replace(/-+$/, '') // Trim - from end of text
220 |
221 | createPage({
222 | path: `poetry/tag/${slug}`,
223 | component: path.resolve(`./src/components/poems/poem-tag.js`),
224 | context: { tag: tag },
225 | })
226 | })
227 | allTags.forEach((allTag) => {
228 | let tag = allTag.fieldValue
229 | let slug = tag
230 | .toString()
231 | .toLowerCase()
232 | .replace(/\s+/g, '-') // Replace spaces with -
233 | .replace(/[^\w\-]+/g, '') // Remove all non-word chars
234 | .replace(/\-\-+/g, '-') // Replace multiple - with single -
235 | .replace(/^-+/, '') // Trim - from start of text
236 | .replace(/-+$/, '') // Trim - from end of text
237 |
238 | createPage({
239 | path: `/tag/${slug}`,
240 | component: path.resolve(`./src/components/tags/tag.js`),
241 | context: { tag: tag },
242 | })
243 | })
244 |
245 | //Create article tags index
246 | createPage({
247 | path: 'poetry/tags',
248 | component: path.resolve(`./src/components/poems/poem-tags.js`),
249 | context: { tags: poemTags },
250 | })
251 | createPage({
252 | path: 'articles/tags',
253 | component: path.resolve(`./src/components/posts/post-tags.js`),
254 | context: { tags: articleTags },
255 | })
256 | createPage({
257 | path: 'blog/tags',
258 | component: path.resolve(`./src/components/blog/blog-tags.js`),
259 | context: { tags: blogTags },
260 | })
261 | createPage({
262 | path: '/tags',
263 | component: path.resolve(`./src/components/tags/tags.js`),
264 | context: { tags: allTags },
265 | })
266 | }
267 |
--------------------------------------------------------------------------------
/lint-staged.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | '*.{js,json}': ['eslint --fix', 'prettier --write'],
3 | }
4 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | publish = "public"
3 |
4 | [[plugins]]
5 | package = "netlify-plugin-gatsby-cache"
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "dschapman-com",
4 | "version": "0.0.6",
5 | "author": "D.S. Chapman",
6 | "license": "MIT",
7 | "scripts": {
8 | "build": "GATSBY_EXPERIMENTAL_PAGE_BUILD_ON_DATA_CHANGES=true gatsby build --log-pages",
9 | "clean": "gatsby clean",
10 | "develop": "gatsby develop",
11 | "format": "prettier --write \"**/*.js{,on}\" \"**/*.md\" \"**/*.mdx\"",
12 | "lint": "eslint . --fix --ext .js,.md,.mdx",
13 | "start": "gatsby develop",
14 | "test": "jest"
15 | },
16 | "dependencies": {
17 | "@emotion/core": "^11.0.0",
18 | "@emotion/eslint-plugin": "^11.2.0",
19 | "@emotion/react": "^11.4.1",
20 | "@emotion/styled": "^11.3.0",
21 | "@mdx-js/mdx": "^1.5.8",
22 | "@mdx-js/react": "^1.5.8",
23 | "@tippyjs/react": "^4.0.2",
24 | "babel-plugin-emotion": "^11.0.0",
25 | "gatsby": "^3.13.0",
26 | "gatsby-cli": "^3.13.0",
27 | "gatsby-image": "^3.11.0",
28 | "gatsby-plugin-feed": "^3.0",
29 | "gatsby-plugin-google-analytics": "^3.13.0",
30 | "gatsby-plugin-mdx": "^2.13.0",
31 | "gatsby-plugin-netlify": "^3.13.0",
32 | "gatsby-plugin-react-helmet": "^4.13.0",
33 | "gatsby-plugin-sharp": "^3.13.0",
34 | "gatsby-plugin-sitemap": "^4.9.0",
35 | "gatsby-plugin-twitter": "^3.13.0",
36 | "gatsby-remark-copy-linked-files": "^4.10.0",
37 | "gatsby-remark-double-brackets-link": "^0.1.8",
38 | "gatsby-remark-images": "^5.10.0",
39 | "gatsby-remark-mermaid": "^2.1.0",
40 | "gatsby-remark-numbered-footnotes": "^1.0.1",
41 | "gatsby-remark-prismjs": "^5.10.0",
42 | "gatsby-remark-slug": "^0.1.0",
43 | "gatsby-remark-smartypants": "^4.10.0",
44 | "gatsby-source-filesystem": "^3.13.0",
45 | "gatsby-transformer-markdown-references": "^0.1.5",
46 | "gatsby-transformer-remark": "^5.4.0",
47 | "gatsby-transformer-sharp": "^3.13.0",
48 | "js-yaml": "^4.0.0",
49 | "lodash": "^4.17.15",
50 | "path": "^0.12.7",
51 | "prismjs": "^1.20.0",
52 | "prop-types": "^15.7.2",
53 | "puppeteer": "^13.0.1",
54 | "react": "^17.0.2",
55 | "react-dom": "^17.0.2",
56 | "react-helmet": "^6.0.0",
57 | "remark-slug": "^7.0.0",
58 | "sharp": "^0.31.3",
59 | "slugify": "^1.4.0",
60 | "yarn": "^1.22.4"
61 | },
62 | "devDependencies": {
63 | "@iconify/icons-mdi": "^1.0.105",
64 | "@iconify/react": "^3.0.1",
65 | "@testing-library/react": "^12.0.0",
66 | "eslint": "^7.0.0",
67 | "eslint-config-prettier": "^8.3.0",
68 | "eslint-plugin-import": "^2.20.2",
69 | "eslint-plugin-mdx": "^1.6.8",
70 | "eslint-plugin-prettier": "^3.1.2",
71 | "eslint-plugin-react": "^7.20.0",
72 | "husky": "^7.0.2",
73 | "jest": "^27.0.6",
74 | "lint-staged": "^11.1.2",
75 | "prettier": "^2.0.2"
76 | },
77 | "husky": {
78 | "hooks": {
79 | "pre-commit": "lint-staged"
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | proseWrap: 'preserve',
3 | singleQuote: true,
4 | trailingComma: 'es5',
5 | printWidth: 80,
6 | semi: false,
7 | jsxBracketSameLine: true,
8 | }
9 |
--------------------------------------------------------------------------------
/src/components/audio.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Audio = ({ src }) => {
4 | return src ? (
5 |
6 |
7 |
8 | ) : (
9 | <>>
10 | )
11 | }
12 |
13 | export default Audio
14 |
--------------------------------------------------------------------------------
/src/components/avatar.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React from 'react'
3 | import { jsx, css } from '@emotion/react'
4 | import avatar from '../../dschapman-com-content/assets/avatar.jpg'
5 |
6 | const Avatar = () => {
7 | return (
8 |
9 |
22 |
23 | )
24 | }
25 |
26 | export default Avatar
27 |
--------------------------------------------------------------------------------
/src/components/blog/blog-index.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React from 'react'
3 | import { css, jsx } from '@emotion/react'
4 | import styled from '@emotion/styled'
5 | import { graphql, useStaticQuery } from 'gatsby'
6 | import BlogList from './blog-list'
7 |
8 | export default function BlogIndex() {
9 | const data = useStaticQuery(graphql`
10 | query {
11 | allMdx(
12 | filter: {
13 | frontmatter: { published: { eq: true } }
14 | fileAbsolutePath: { regex: "/content/blog/" }
15 | }
16 | sort: { fields: frontmatter___date, order: DESC }
17 | limit: 1000
18 | ) {
19 | edges {
20 | node {
21 | id
22 | slug
23 | frontmatter {
24 | excerpt
25 | title
26 | date
27 | tags
28 | published
29 | }
30 | excerpt
31 | }
32 | }
33 | }
34 | }
35 | `)
36 | return
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/blog/blog-layout.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React from 'react'
3 | import { css, jsx } from '@emotion/react'
4 | import { MDXRenderer } from 'gatsby-plugin-mdx'
5 | import SimilarContent from '../similar-content.js'
6 | import Layout from '../layout/layout'
7 | import { graphql } from 'gatsby'
8 |
9 | const Post = ({ data, pageContext, location }) => {
10 | return (
11 |
17 |
22 | {data.mdx.frontmatter.title}
23 |
24 |
25 |
26 | {new Date(data.mdx.frontmatter.date).toLocaleDateString(undefined, {
27 | year: 'numeric',
28 | month: 'long',
29 | day: 'numeric',
30 | })}
31 |
32 |
33 | {data.mdx.body}
34 |
38 |
39 | )
40 | }
41 |
42 | export const pageQuery = graphql`
43 | query BlogQuery($id: String) {
44 | mdx(id: { eq: $id }) {
45 | id
46 | body
47 | frontmatter {
48 | title
49 | excerpt
50 | tags
51 | date
52 | }
53 | }
54 | }
55 | `
56 | export default Post
57 |
--------------------------------------------------------------------------------
/src/components/blog/blog-list.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React from 'react'
3 | import styled from '@emotion/styled'
4 | import { jsx, css } from '@emotion/react'
5 | import { InternalLink } from '../layout/links'
6 | import { bpMaxLG, bpMaxSM } from '../../lib/breakpoints'
7 | import colors from '../../lib/colors'
8 | import { Link } from 'gatsby'
9 |
10 | export const BlogListStyled = styled.ul`
11 | list-style-type: none;
12 | padding: 4px 0px;
13 | margin: 0;
14 | ${bpMaxLG} {
15 | width: 100%;
16 | }
17 | `
18 |
19 | export const BlogLinkStyled = styled.li`
20 | padding: 0.5rem 0.5rem 0.5rem 0;
21 | margin: 0 0rem 0.5rem 0rem;
22 | display: block;
23 | width: fit-content;
24 | height: fit-content;
25 | box-shadow: 0px 0px 1px 1px rgba(0, 0, 0, 0.01);
26 | transition: all 200ms ease-in-out;
27 | border-radius: 0 5px 5px 0;
28 |
29 | &:hover {
30 | box-shadow: 1px 4px 10px 3px rgba(28, 98, 103, 0.3);
31 | transform: scale(1.02);
32 | border-left: 4px solid ${colors.blue};
33 | padding: 0.5rem 0.5rem 0.5rem 0.5rem;
34 | }
35 |
36 | ul {
37 | list-style-type: none;
38 | margin: 0;
39 | padding: 0;
40 | }
41 | li {
42 | display: inline;
43 | word-break: break-word;
44 | margin: 0;
45 | padding: 0;
46 | }
47 | `
48 |
49 | const TagLink = styled(Link)`
50 | font-style: italic;
51 | font-size: 1rem;
52 | text-decoration: none;
53 | color: ${colors.gray};
54 | border-radius: 5px;
55 | padding: 4px 0px;
56 | margin-right: 1rem;
57 | &:hover {
58 | color: ${colors.red};
59 | }
60 | `
61 |
62 | export const Description = styled.div`
63 | font-size: 1.4rem;
64 | padding: 0.5 0;
65 | color: ${colors.gray};
66 | `
67 |
68 | export const PubDate = styled.h3`
69 | font-size: 1.4rem;
70 | padding: 0;
71 | margin: 0.5rem 0;
72 | color: ${colors.text};
73 | `
74 |
75 | export default ({ posts, format }) => {
76 | return (
77 |
89 | {posts.map(({ node: post }) => (
90 |
91 |
96 | {post.frontmatter.title}
97 |
98 |
99 |
100 | {new Date(post.frontmatter.date).toDateString()}
101 |
102 |
103 | {post.frontmatter.tags.map((tag) => (
104 |
105 |
114 | {tag}
115 |
116 |
117 | ))}
118 |
119 |
120 | ))}
121 |
122 | )
123 | }
124 |
--------------------------------------------------------------------------------
/src/components/blog/blog-tag.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React from 'react'
3 | import { jsx } from '@emotion/react'
4 | import { graphql } from 'gatsby'
5 | import { InternalLink, InternalNotesLink } from '../layout/links'
6 | import Layout from '../layout/layout'
7 | import BlogList from './blog-list'
8 | import { MDXProvider } from '@mdx-js/react'
9 | import MDXRenderer from 'gatsby-plugin-mdx/mdx-renderer'
10 | import components from '../layout/mdx-components'
11 | import LinktipPreview from '../layout/linktip-preview'
12 |
13 | const BlogTag = ({ data, pageContext, location }) => {
14 | const { tag } = pageContext
15 | const { edges, totalCount } = data.articles
16 | let note = data.notes
17 | if (note != null) {
18 | let references = []
19 | let referenceBlock
20 | if (note.inboundReferenceNotes != null) {
21 | references = note.inboundReferenceNotes.map((ref) => (
22 |
23 |
24 | {ref.title}
25 |
26 |
27 | ))
28 | }
29 |
30 | if (references.length > 0) {
31 | referenceBlock = (
32 | <>
33 | Referenced in
34 |
35 | >
36 | )
37 | }
38 | return (
39 |
44 |
45 |
46 |
50 | {note.title}
51 | {note.childMdx.body}
52 | {referenceBlock}
53 |
54 | }>
55 |
56 | my notes on {tag}
57 |
58 |
59 |
60 | See all tags →
61 |
62 | )
63 | } else {
64 | return (
65 |
70 |
71 |
72 | See all tags →
73 |
74 | )
75 | }
76 | }
77 |
78 | export default BlogTag
79 |
80 | export const pageQuery = graphql`
81 | query BlogTag($tag: String) {
82 | articles: allMdx(
83 | limit: 2000
84 | sort: { fields: [frontmatter___date], order: DESC }
85 | filter: {
86 | frontmatter: { tags: { in: [$tag] } }
87 | fileAbsolutePath: { regex: "/content/blog/" }
88 | }
89 | ) {
90 | totalCount
91 | edges {
92 | node {
93 | slug
94 | frontmatter {
95 | title
96 | date
97 | excerpt
98 | tags
99 | }
100 | body
101 | }
102 | }
103 | }
104 | }
105 | `
106 |
--------------------------------------------------------------------------------
/src/components/blog/blog-tags.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import Layout from '../layout/layout'
4 | import TagList from '../tags/list'
5 |
6 | const BlogTags = ({ pageContext, location }) => {
7 | const { tags } = pageContext
8 | return (
9 |
14 |
15 |
16 | )
17 | }
18 |
19 | export default BlogTags
20 |
--------------------------------------------------------------------------------
/src/components/book-card.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React from 'react'
3 | import { jsx, css } from '@emotion/react'
4 | import styled from '@emotion/styled'
5 | import book from '../../dschapman-com-content/assets/seasons-of-thought-cover.png'
6 | import { ExternalLink, InternalLink } from './layout/links'
7 | import colors from '../lib/colors'
8 | import { bpMaxLG } from '../lib/breakpoints'
9 |
10 | const Container = styled.div`
11 | display: flex;
12 | flex-wrap: wrap;
13 | width: 55%;
14 | ${bpMaxLG} {
15 | width: 100%;
16 | justify-content: center;
17 | }
18 | padding-bottom: 15px;
19 | `
20 |
21 | const Links = styled.div`
22 | display: flex;
23 | flex-wrap: wrap;
24 | width: 100%;
25 | `
26 |
27 | const Button = styled(ExternalLink)`
28 | padding: 0.66rem;
29 | border: solid 2px ${colors.red};
30 | background-color: ${colors.background};
31 | position: relative;
32 | overflow: hidden;
33 | color: ${colors.red};
34 | font-family: 'proxima-nova';
35 | border-radius: 30px;
36 | text-decoration: none;
37 | transition: 0.2s transform ease-in-out;
38 | margin: 0.5rem 2rem;
39 | white-space: nowrap;
40 | font-size: 1.25rem;
41 |
42 | &:after {
43 | background-color: ${colors.red};
44 | border-radius: 30px;
45 | content: '';
46 | display: block;
47 | height: 100%;
48 | width: 100%;
49 | position: absolute;
50 | left: 0;
51 | top: 0;
52 | transform: translate(-100%, 0) rotate(10deg);
53 | transform-origin: top left;
54 | transition: 0.2s transform ease-out;
55 | will-change: transform;
56 | z-index: -1;
57 | }
58 |
59 | &:hover {
60 | border: solid 2px transparent;
61 | color: ${colors.background};
62 | transform: scale(1.05);
63 | will-change: transform;
64 | &:after {
65 | transform: translate(0, 0);
66 | }
67 | }
68 | `
69 |
70 | const BookCard = ({ description }) => {
71 | return (
72 |
73 |
74 |
87 |
88 |
94 |
95 | Seasons of Thought is a collection of poems about the beauty of human
96 | experience in everyday life. The poems in the collection use the
97 | passage of time as a canvas to explore nature, family, childhood,
98 | faith, and the changing seasons.
99 |
100 |
101 |
102 | Buy the Book
103 |
104 |
105 | Add on Goodreads
106 |
107 |
108 |
109 |
110 | )
111 | }
112 |
113 | export default BookCard
114 |
--------------------------------------------------------------------------------
/src/components/featured.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React from 'react'
3 | import { css, jsx } from '@emotion/react'
4 | import styled from '@emotion/styled'
5 | import { useStaticQuery, graphql, Link } from 'gatsby'
6 | import PostList from './posts/post-list'
7 | import BlogList from './blog/blog-list'
8 |
9 | export default function Featured() {
10 | const data = useStaticQuery(graphql`
11 | query {
12 | posts: allMdx(
13 | limit: 1000
14 | sort: { fields: frontmatter___date, order: DESC }
15 | filter: {
16 | frontmatter: { featured: { eq: true } }
17 | fileAbsolutePath: { regex: "/dschapman-com-content/posts/" }
18 | }
19 | ) {
20 | edges {
21 | node {
22 | frontmatter {
23 | title
24 | slug
25 | excerpt
26 | tags
27 | }
28 | }
29 | }
30 | }
31 | poems: allMdx(
32 | limit: 1000
33 | sort: { fields: frontmatter___title, order: DESC }
34 | filter: {
35 | frontmatter: { featured: { eq: true } }
36 | fileAbsolutePath: { regex: "/dschapman-com-content/poems/" }
37 | }
38 | ) {
39 | edges {
40 | node {
41 | frontmatter {
42 | title
43 | slug
44 | excerpt
45 | tags
46 | }
47 | }
48 | }
49 | }
50 | blogs: allMdx(
51 | limit: 3
52 | sort: { fields: frontmatter___date, order: DESC }
53 | filter: {
54 | frontmatter: { published: { eq: true } }
55 | fileAbsolutePath: { regex: "/dschapman-com-content/blog/" }
56 | }
57 | ) {
58 | edges {
59 | node {
60 | slug
61 | excerpt
62 | frontmatter {
63 | title
64 | date
65 | tags
66 | }
67 | }
68 | }
69 | }
70 | }
71 | `)
72 | return (
73 | <>
74 | Latest Blog Posts
75 |
76 | Featured Articles
77 |
78 | Featured Poems
79 |
80 | >
81 | )
82 | }
83 |
--------------------------------------------------------------------------------
/src/components/global.css:
--------------------------------------------------------------------------------
1 | @import url('https://p.typekit.net/p.css?s=1&k=osf8fyt&ht=tk&f=171.172.175.176.5474.5475.6768.6769.6770.6771.6772.6773&a=18424651&app=typekit&e=css');
2 |
3 | *::selection {
4 | color: #ffffff;
5 | background-color: #75b9be;
6 | height: 100%;
7 | }
8 |
9 | html {
10 | font-size: 16px;
11 | font-family: 'adobe-garamond-pro';
12 | text-rendering: optimizeLegibility;
13 | -webkit-font-smoothing: antialiased;
14 | }
15 |
16 | body {
17 | counter-reset: sidenote-counter;
18 | margin: 0;
19 | }
20 |
21 | header {
22 | display: flex;
23 | align-items: center;
24 | padding-top: 3rem;
25 | margin: 0 75px;
26 | justify-content: center;
27 | }
28 |
29 | header > div {
30 | display: flex;
31 | align-items: center;
32 | width: 85%;
33 | }
34 |
35 | footer {
36 | right: 0;
37 | margin-top: 5px;
38 | display: flex;
39 | flex-direction: column;
40 | flex-grow: 1;
41 | padding: 3rem;
42 | font-family: 'proxima-nova';
43 | font-weight: 100;
44 | font-size: 2rem;
45 | }
46 |
47 | h1,
48 | h2,
49 | h3,
50 | h4,
51 | h5,
52 | h6 {
53 | font-family: 'proxima-nova';
54 | font-weight: 100;
55 | }
56 |
57 | h1 {
58 | font-size: 3rem;
59 | line-height: 5rem;
60 | font-weight: 300;
61 | }
62 |
63 | h2 {
64 | display: inline-block;
65 | font-size: 2.25rem;
66 | font-weight: 300;
67 | padding-bottom: 5px;
68 | margin: 1rem 0;
69 | border-bottom: 2px solid #75b9be;
70 | }
71 | .post h2 {
72 | margin: 1.5rem 0;
73 | }
74 |
75 | h3 {
76 | font-size: 2.25rem;
77 | }
78 |
79 | p,
80 | dl,
81 | ol,
82 | ul,
83 | div {
84 | font-size: 1.4rem;
85 | line-height: 2rem;
86 | }
87 |
88 | p {
89 | margin-top: 1.4rem;
90 | margin-bottom: 1.4rem;
91 | padding-right: 0;
92 | vertical-align: baseline;
93 | }
94 |
95 | blockquote p {
96 | width: 55%;
97 | margin-right: 40px;
98 | }
99 |
100 | main {
101 | margin: 0 75px;
102 | display: flex;
103 | flex-grow: 1;
104 | flex-direction: row;
105 | justify-content: center;
106 | }
107 |
108 | article {
109 | width: 87.5%;
110 | }
111 |
112 | article > h1,
113 | article > h2,
114 | article > h3,
115 | article > h4,
116 | article > h5,
117 | article > h6 {
118 | max-width: 70%;
119 | }
120 |
121 | article > p,
122 | article > ol,
123 | article > footer,
124 | article > table,
125 | article > ul {
126 | width: 55%;
127 | }
128 |
129 | hr {
130 | border: 0;
131 | border-bottom: 1px solid;
132 | border-color: #f6f6f6;
133 | }
134 |
135 | @media (max-width: 1199px) {
136 | html {
137 | font-size: 16px;
138 | }
139 |
140 | main {
141 | margin: 0 40px;
142 | flex: 1;
143 | }
144 |
145 | header {
146 | margin: 0 40px;
147 | }
148 |
149 | article > h1,
150 | article > h2,
151 | article > h3,
152 | article > h4,
153 | article > h5,
154 | article > h6 {
155 | max-width: 100%;
156 | }
157 |
158 | hr,
159 | article > p,
160 | article > footer,
161 | article > table {
162 | width: 100%;
163 | }
164 | blockquote p {
165 | width: 100%;
166 | }
167 | }
168 |
169 | @media (max-width: 767px) {
170 | html {
171 | font-size: 15px;
172 | }
173 | }
174 |
175 | @media (max-width: 544px) {
176 | html {
177 | font-size: 14px;
178 | }
179 | article {
180 | width: 90%;
181 | }
182 | }
183 |
184 | @media (max-width: 400px) {
185 | article {
186 | width: 100%;
187 | }
188 | header {
189 | margin: 0 5px;
190 | }
191 | header > div {
192 | width: 100%;
193 | }
194 | main {
195 | margin: 0 5px;
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/src/components/latest-articles.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React from 'react'
3 | import { css, jsx } from '@emotion/react'
4 | import styled from '@emotion/styled'
5 | import { useStaticQuery, graphql, Link } from 'gatsby'
6 | import PostList from './posts/post-list'
7 |
8 | export default function LatestArticles() {
9 | const data = useStaticQuery(graphql`
10 | query {
11 | posts: allMdx(
12 | limit: 5
13 | sort: { fields: frontmatter___date, order: DESC }
14 | filter: { fileAbsolutePath: { regex: "/dschapman-com-content/posts/" } }
15 | ) {
16 | edges {
17 | node {
18 | frontmatter {
19 | title
20 | slug
21 | excerpt
22 | tags
23 | }
24 | }
25 | }
26 | }
27 | }
28 | `)
29 | return
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/layout/TextStyles.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from '@emotion/styled'
3 | import { bpMaxLG } from '../../lib/breakpoints'
4 |
5 | export const Callout = styled.blockquote`
6 | ::before {
7 | content: '';
8 | display: block;
9 | margin: 0 auto;
10 | width: 3rem;
11 | border-left: none;
12 | border-top: 2px solid #75b9be;
13 | margin-bottom: 0.5em;
14 | }
15 | ::after {
16 | content: '';
17 | display: block;
18 | margin: 0 auto;
19 | width: 3rem;
20 | border-top: 2px solid #75b9be;
21 | margin-top: 0.5em;
22 | }
23 | width: 55%;
24 | text-align: center;
25 | font-size: 2rem;
26 | lineheight: 2rem;
27 | display: block;
28 | ${bpMaxLG} {
29 | margin: 0 auto;
30 | }
31 | `
32 | export const Blockquote = styled.blockquote`
33 | border-left: 10px solid #75b9be;
34 | margin: 1.5rem 10px;
35 | padding: 0 20px;
36 | font-size: 1.4rem;
37 | `
38 |
--------------------------------------------------------------------------------
/src/components/layout/figure.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React from 'react'
3 | import { jsx, css } from '@emotion/react'
4 | import { bpMaxLG } from '../../lib/breakpoints'
5 | import colors from '../../lib/colors'
6 |
7 | function getCaption(caption) {
8 | if (caption == undefined) {
9 | return <>>
10 | } else {
11 | return {caption}
12 | }
13 | }
14 |
15 | const Figure = ({ children, caption, src, alt, fullwidth }) => {
16 | let Caption = getCaption(caption)
17 | const figureStyles = css`
18 | img {
19 | width: 100%;
20 | }
21 |
22 | figure {
23 | padding: 0;
24 | border: 0;
25 | font-size: 100%;
26 | font: inherit;
27 | vertical-align: baseline;
28 | max-width: 55%;
29 | -webkit-margin-start: 0;
30 | -webkit-margin-end: 0;
31 | margin: 0 0 3em 0;
32 | }
33 |
34 | figcaption {
35 | float: right;
36 | clear: right;
37 | margin-top: 0;
38 | margin-bottom: 0;
39 | font-size: 1.1rem;
40 | line-height: 1.4;
41 | vertical-align: baseline;
42 | position: relative;
43 | color: ${colors.gray};
44 | }
45 |
46 | figure.fullwidth figcaption {
47 | margin-right: 24%;
48 | }
49 | ${bpMaxLG} {
50 | figure {
51 | max-width: 90%;
52 | }
53 |
54 | figcaption,
55 | figure.fullwidth figcaption {
56 | margin-right: 0%;
57 | max-width: none;
58 | }
59 | img {
60 | width: 100%;
61 | }
62 | }
63 | `
64 | if (fullwidth == false) {
65 | return (
66 |
67 |
68 | {children}
69 |
70 | {Caption}
71 |
72 |
73 | )
74 | } else {
75 | return (
76 |
77 |
78 | {children}
79 |
80 | {Caption}
81 |
82 |
83 | )
84 | }
85 | }
86 |
87 | export { Figure }
88 |
--------------------------------------------------------------------------------
/src/components/layout/footer.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React from 'react'
3 | import styled from '@emotion/styled'
4 | import { css, jsx } from '@emotion/react'
5 | import colors from '../../lib/colors'
6 |
7 | const Footer = () => {
8 | const FooterContainer = styled.footer`
9 | background: ${colors.bluegreen};
10 | `
11 | return (
12 |
13 |
14 | Contact
15 |
32 |
38 | Content is copyrighted 2019-2024 © D.S. Chapman. This site was built
39 | using GatsbyJS. Code for this website is open source and available on{' '}
40 |
Github
41 |
42 |
43 |
44 | )
45 | }
46 |
47 | const FooterContent = styled.footer`
48 | margin: 0 auto;
49 | padding-right: 12.5%;
50 | a {
51 | text-decoration: underline;
52 | text-decoration-thickness: 1px;
53 | color: white;
54 | &:hover {
55 | text-decoration: none;
56 | }
57 | }
58 | `
59 |
60 | export default Footer
61 |
--------------------------------------------------------------------------------
/src/components/layout/header-links.js:
--------------------------------------------------------------------------------
1 | /**@jsx jsx */
2 | import React from 'react'
3 | import { css, jsx } from '@emotion/react'
4 | import styled from '@emotion/styled'
5 | import { Link } from 'gatsby'
6 | import colors from '../../lib/colors'
7 | import { bpMaxLG, bpMaxMD, bpMaxSM, bpMaxXS } from '../../lib/breakpoints'
8 | const Nav = styled.nav`
9 | display: flex;
10 | width: 100%;
11 | `
12 |
13 | const NavLink = styled(Link)`
14 | padding: 1rem;
15 | font-size: 1.4rem;
16 | color: ${colors.text};
17 | text-decoration: none;
18 | text-transform: uppercase;
19 | letter-spacing: 3px;
20 | &:hover {
21 | text-decoration: underline;
22 | text-decoration-thickness: 1px;
23 | }
24 |
25 | ${bpMaxLG} {
26 | font-size: 1.4rem;
27 | padding: 0.75rem;
28 | }
29 | ${bpMaxMD} {
30 | font-size: 1rem;
31 | }
32 | ${bpMaxSM} {
33 | font-size: 0.75rem;
34 | }
35 | `
36 |
37 | export default () => {
38 | return (
39 |
40 | D.S. Chapman
41 |
52 | Articles
53 |
54 |
65 | Poetry
66 |
67 |
78 | Blog
79 |
80 |
81 | )
82 | }
83 |
--------------------------------------------------------------------------------
/src/components/layout/header.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import SEO from '../seo'
3 | import Logo from './logo'
4 | import HeaderLinks from './header-links'
5 | import { useStaticQuery, graphql } from 'gatsby'
6 | import styled from '@emotion/styled'
7 |
8 | import { bpMaxLG, bpMaxSM, bpMaxXS } from '../../lib/breakpoints'
9 |
10 | export const Header = styled.header`
11 | ${bpMaxLG} {
12 | padding-top: 2rem;
13 | }
14 | ${bpMaxSM} {
15 | padding-top: 1rem;
16 | }
17 | ${bpMaxXS} {
18 | padding-top: 0.5rem;
19 | }
20 | `
21 |
22 | export default ({
23 | title,
24 | description,
25 | seoTitle,
26 | seoTitleAddition1,
27 | seoTitleAddition2,
28 | type,
29 | location,
30 | }) => {
31 | const {
32 | site: { siteMetadata },
33 | } = useStaticQuery(graphql`
34 | {
35 | site {
36 | siteMetadata {
37 | title
38 | }
39 | }
40 | }
41 | `)
42 |
43 | if (seoTitle) {
44 | title = seoTitle
45 | } else {
46 | title = title
47 | }
48 | return (
49 |
50 |
51 |
52 |
53 |
61 |
62 |
63 | )
64 | }
65 |
--------------------------------------------------------------------------------
/src/components/layout/layout.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import '../global.css'
3 | import { MDXProvider } from '@mdx-js/react'
4 | import components from './mdx-components-with-previews'
5 | import Header from './header'
6 | import Footer from './footer'
7 | import Tooltip from './tooltip'
8 | import { Footnote, Marginnote } from './sidenote'
9 | import { Figure } from './figure'
10 | import Linktip from './linktip'
11 | import { Callout } from './TextStyles'
12 | import { useStaticQuery, graphql } from 'gatsby'
13 | import styled from '@emotion/styled'
14 | import NoteNav from '../notes/note-nav'
15 |
16 | export const useAllMdx = () => {
17 | return useStaticQuery(graphql`
18 | query AllMdx {
19 | allMdx(
20 | filter: {
21 | fileAbsolutePath: { regex: "/dschapman-com-content|My-notes/" }
22 | }
23 | ) {
24 | nodes {
25 | frontmatter {
26 | slug
27 | title
28 | id
29 | published
30 | tags
31 | }
32 | slug
33 | body
34 | fileAbsolutePath
35 | }
36 | }
37 | }
38 | `)
39 | }
40 |
41 | export const Root = styled.root``
42 | export const Main = styled.main`
43 | padding: 1rem 0rem;
44 | `
45 |
46 | export default ({
47 | children,
48 | title,
49 | description,
50 | seoTitle,
51 | seoTitleAddition1,
52 | seoTitleAddition2,
53 | type,
54 | location,
55 | className,
56 | }) => {
57 | const data = useAllMdx()
58 | const popups = {}
59 | const posts = data.allMdx.nodes
60 | posts.map((post) => {
61 | if (post) {
62 | popups[`${post.slug.substring(post.slug.lastIndexOf('/') + 1)}`] = {
63 | title: post.frontmatter.title,
64 | body: post.body,
65 | slug: post.frontmatter.slug,
66 | dendronId: post.frontmatter.id,
67 | published: post.frontmatter.published,
68 | }
69 | }
70 | })
71 | const NoteBlock = () => {
72 | if (type === 'Note 📝') {
73 | return
74 | } else {
75 | return <>>
76 | }
77 | }
78 |
79 | const AnchorTag = (props) =>
80 | return (
81 |
82 |
91 |
92 |
93 |
94 |
95 | {title}
96 | ,
100 | Tooltip: (props) => ,
101 | Linktip: (props) => ,
102 | Callout: (props) => ,
103 | Marginnote: (props) => ,
104 | Figure: (props) => ,
105 | }}>
106 | {children}
107 |
108 |
109 |
110 |
111 |
112 | )
113 | }
114 |
--------------------------------------------------------------------------------
/src/components/layout/links.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from '@emotion/styled'
3 | import { Link } from 'gatsby'
4 | import colors from '../../lib/colors'
5 |
6 | export const InternalNotesLink = styled(Link)`
7 | background: ${colors.lightblue};
8 | text-decoration: none;
9 | color: ${colors.text};
10 | &:hover,
11 | &:focus {
12 | color: ${colors.text};
13 | background: white;
14 | text-decoration: underline;
15 | text-decoration-color: ${colors.lightblue};
16 | text-decoration-thickness: 1px;
17 | }
18 | `
19 | export const InternalLink = styled(Link)`
20 | color: ${colors.text};
21 | text-decoration-color: ${colors.bluegreen};
22 | text-decoration-thickness: 1px;
23 | &:hover {
24 | color: ${colors.hcbluegreen};
25 | text-decoration-color: ${colors.bluegreen};
26 | }
27 | `
28 |
29 | export const ExternalLink = styled(Link)`
30 | text-decoration-color: ${colors.red};
31 | text-decoration-thickness: 1px;
32 | color: ${colors.text};
33 | &:hover {
34 | color: ${colors.red};
35 | text-decoration-color: ${colors.red};
36 | }
37 | `
38 |
--------------------------------------------------------------------------------
/src/components/layout/linktip-preview.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React, { forwardRef } from 'react'
3 | import Tippy from '@tippyjs/react'
4 | import { css, jsx } from '@emotion/react'
5 | import 'tippy.js/dist/tippy.css'
6 | import 'tippy.js/animations/shift-away.css'
7 | import 'tippy.js/themes/light.css'
8 | import './tippyBox.css'
9 | import { bpMaxLG } from '../../lib/breakpoints'
10 |
11 | const LinktipPreview = forwardRef((props, ref) => {
12 | let placement
13 | let multiple
14 | if (props.placement) {
15 | placement = props.placement
16 | } else {
17 | placement = 'top'
18 | }
19 | if (props.multiple) {
20 | multiple = props.multiple
21 | } else {
22 | multiple = true
23 | }
24 | return (
25 |
47 |
57 | {props.children}
58 |
59 |
60 | )
61 | })
62 | export default LinktipPreview
63 |
--------------------------------------------------------------------------------
/src/components/layout/linktip.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React, { forwardRef } from 'react'
3 | import Tippy from '@tippyjs/react'
4 | import { css, jsx } from '@emotion/react'
5 | import colors from '../../lib/colors'
6 | import 'tippy.js/dist/tippy.css'
7 | import 'tippy.js/animations/shift-away.css'
8 | import 'tippy.js/themes/light.css'
9 |
10 | const Linktip = forwardRef((props, ref) => {
11 | let placement
12 | let multiple
13 | if (props.placement) {
14 | placement = props.placement
15 | } else {
16 | placement = 'top'
17 | }
18 | if (props.multiple) {
19 | multiple = props.multiple
20 | } else {
21 | multiple = true
22 | }
23 | return (
24 |
38 |
47 | {props.children}
48 |
49 |
50 | )
51 | })
52 |
53 | export default Linktip
54 |
--------------------------------------------------------------------------------
/src/components/layout/logo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'gatsby'
3 |
4 | // npm install --save-dev @iconify/react @iconify/icons-mdi
5 | import { Icon } from '@iconify/react'
6 | import sproutIcon from '@iconify/icons-mdi/sprout'
7 |
8 | export default () => (
9 |
10 |
11 |
12 | )
13 |
--------------------------------------------------------------------------------
/src/components/layout/mdx-components-with-previews.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React from 'react'
3 | import { jsx } from '@emotion/react'
4 | import { isString, isEmpty } from 'lodash'
5 |
6 | import Linktip from './linktip'
7 | import LinktipPreview from './linktip-preview'
8 | import MDXRenderer from 'gatsby-plugin-mdx/mdx-renderer'
9 | import components from './mdx-components'
10 | import { MDXProvider } from '@mdx-js/react'
11 | import Tooltip from './tooltip'
12 | import { Footnote, Marginnote } from './sidenote'
13 | import { Figure } from './figure'
14 | import { Blockquote, Callout } from './TextStyles'
15 | import { InternalLink, InternalNotesLink, ExternalLink } from './links'
16 |
17 | const INTERNAL_LINK_REGEX = /^\/notes/g
18 | const INTERNAL_NON_NOTES_LINK_REGEX = /^\/(?!notes)/g
19 | const AnchorTag = ({ href, popups, ...restProps }) => {
20 | const isInternalNotesLink = !isEmpty(href.match(INTERNAL_LINK_REGEX))
21 | const isInternalLink = !isEmpty(href.match(INTERNAL_NON_NOTES_LINK_REGEX))
22 | let renderedLink = restProps.children
23 | if (isString(restProps.children)) {
24 | renderedLink = restProps.children.replace(/\[\[(.*?)\]\]/g, '$1')
25 | }
26 | if (isInternalNotesLink) {
27 | if (renderedLink.includes('|')) {
28 | renderedLink = renderedLink.substring(
29 | renderedLink.lastIndexOf('|') + 1,
30 | renderedLink.length
31 | )
32 | }
33 | if (isEmpty(popups[`${href.substring(href.lastIndexOf('/') + 1)}`])) {
34 | return (
35 |
37 | {renderedLink}
38 |
39 | )
40 | } else {
41 | //check if the note is published, if it's not go to a specific 404 page
42 | if (
43 | popups[`${href.substring(href.lastIndexOf('/') + 1)}`].published != true
44 | ) {
45 | return {renderedLink}
46 | } else {
47 | return (
48 |
52 |
53 | {popups[`${href.substring(href.lastIndexOf('/') + 1)}`].title}
54 |
55 |
56 | {popups[`${href.substring(href.lastIndexOf('/') + 1)}`].body}
57 |
58 |
59 | }
60 | placement="right"
61 | multiple={false}>
62 |
66 | {renderedLink}
67 |
68 |
69 | )
70 | }
71 | }
72 | } else if (isInternalLink) {
73 | if (isEmpty(popups[`${href.substring(href.lastIndexOf('/') + 1)}`])) {
74 | return {renderedLink}
75 | } else {
76 | return (
77 |
81 |
82 | {popups[`${href.substring(href.lastIndexOf('/') + 1)}`].title}
83 |
84 |
85 | {popups[`${href.substring(href.lastIndexOf('/') + 1)}`].body}
86 |
87 |
88 | }
89 | placement="right"
90 | multiple={false}>
91 | {renderedLink}
92 |
93 | )
94 | }
95 | } else {
96 | return (
97 |
98 | {renderedLink}
99 |
100 | )
101 | }
102 | }
103 |
104 | export default {
105 | a: AnchorTag,
106 | blockquote: Blockquote,
107 | img: (props) => ,
108 | Footnote: (props) => ,
109 | Tooltip: (props) => ,
110 | Linktip: (props) => ,
111 | Callout: (props) => ,
112 | Marginnote: (props) => ,
113 | Figure: (props) => ,
114 | }
115 |
--------------------------------------------------------------------------------
/src/components/layout/mdx-components.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { isEmpty } from 'lodash'
4 | import Tooltip from './tooltip'
5 | import { Footnote, Marginnote } from './sidenote'
6 | import { Figure } from './figure'
7 | import Linktip from './linktip'
8 | import { Callout, Blockquote } from './TextStyles'
9 |
10 | import { InternalLink, InternalNotesLink, ExternalLink } from './links'
11 |
12 | const INTERNAL_LINK_REGEX = /^\/notes/g
13 | const INTERNAL_NON_NOTES_LINK_REGEX = /^\/(?!notes)/g
14 | const AnchorTag = (props) => {
15 | const isInternalNotesLink = !isEmpty(props.href.match(INTERNAL_LINK_REGEX))
16 | const isInternalLink = !isEmpty(
17 | props.href.match(INTERNAL_NON_NOTES_LINK_REGEX)
18 | )
19 | let renderedLink = props.children
20 |
21 | if (isInternalNotesLink) {
22 | renderedLink = renderedLink.replace(/\[\[(.*?)\]\]/g, '$1')
23 | renderedLink = renderedLink.substring(
24 | renderedLink.lastIndexOf('|') + 1,
25 | renderedLink.length
26 | )
27 | return {renderedLink}
28 | } else if (isInternalLink) {
29 | return {renderedLink}
30 | } else {
31 | return {renderedLink}
32 | }
33 | }
34 |
35 | export default {
36 | a: AnchorTag,
37 | blockquote: Blockquote,
38 | img: (props) => ,
39 | Footnote: (props) => ,
40 | Tooltip: (props) => ,
41 | Linktip: (props) => ,
42 | Callout: (props) => ,
43 | Marginnote: (props) => ,
44 | Figure: (props) => ,
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/layout/sidenote.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React from 'react'
3 | import { jsx, css } from '@emotion/react'
4 | import { bpMaxLG } from '../../lib/breakpoints'
5 | import colors from '../../lib/colors'
6 | import { random } from 'lodash'
7 |
8 | function getRandomInt(max) {
9 | return Math.floor(Math.random() * max)
10 | }
11 |
12 | const Footnote = ({ count, children }) => {
13 | if (count == undefined) {
14 | count = getRandomInt(3000)
15 | }
16 | const footnoteStyles = css`
17 | .sidenote,
18 | .marginnote {
19 | float: right;
20 | clear: right;
21 | margin-right: -60%;
22 | width: 50%;
23 | margin-top: 0.3rem;
24 | margin-bottom: 0px;
25 | font-size: 1.1rem;
26 | opacity: 85%;
27 | line-height: 1.3;
28 | vertical-align: baseline;
29 | position: relative;
30 | border-left: 2px solid ${colors.bluegreen};
31 | padding-left: 1rem;
32 | }
33 |
34 | .sidenote-number {
35 | counter-increment: sidenote-counter;
36 | }
37 |
38 | .sidenote-number:after,
39 | .sidenote:before {
40 | position: relative;
41 | vertical-align: baseline;
42 | }
43 |
44 | .sidenote-number:after {
45 | content: counter(sidenote-counter);
46 | font-size: 0.9em;
47 | top: -0.5rem;
48 | left: 0.1rem;
49 | padding-right: 3px;
50 | color: ${colors.gray};
51 | }
52 |
53 | .sidenote:before {
54 | content: counter(sidenote-counter) ' ';
55 | font-size: 1rem;
56 | top: -0.5rem;
57 | padding-right: 8px;
58 | }
59 |
60 | blockquote .sidenote,
61 | blockquote .marginnote {
62 | margin-right: -82%;
63 | min-width: 59%;
64 | text-align: left;
65 | }
66 |
67 | label.sidenote-number {
68 | display: inline;
69 | }
70 |
71 | label.margin-toggle:not(.sidenote-number) {
72 | display: none;
73 | }
74 | input.margin-toggle {
75 | display: none;
76 | }
77 | ${bpMaxLG} {
78 | label.margin-toggle:not(.sidenote-number) {
79 | display: inline;
80 | }
81 |
82 | .sidenote,
83 | .marginnote {
84 | display: none;
85 | }
86 |
87 | .margin-toggle:checked + .sidenote,
88 | .margin-toggle:checked + .marginnote {
89 | display: block;
90 | float: left;
91 | left: 1rem;
92 | clear: both;
93 | width: 95%;
94 | margin: 1rem 2.5%;
95 | vertical-align: baseline;
96 | position: relative;
97 | }
98 | label {
99 | cursor: pointer;
100 | }
101 | }
102 | `
103 |
104 | return (
105 |
106 |
109 |
110 | {children}
111 |
112 | )
113 | }
114 |
115 | const Marginnote = ({ count, children }) => {
116 | if (count == undefined) {
117 | count = getRandomInt(3000)
118 | }
119 | const marginnoteStyles = css`
120 | .marginnote {
121 | float: right;
122 | clear: right;
123 | margin-right: -60%;
124 | width: 50%;
125 | margin-top: 0.3rem;
126 | margin-bottom: 0;
127 | font-size: 1.1rem;
128 | opacity: 85%;
129 | line-height: 1.3;
130 | vertical-align: baseline;
131 | position: relative;
132 | border-left: 2px solid ${colors.red};
133 | padding-left: 1em;
134 | }
135 |
136 | .marginnote-marker {
137 | font-size: 0.9em;
138 | top: -0.5rem;
139 | left: 0em;
140 | padding-right: 3px;
141 | padding-left: 3px;
142 | color: ${colors.gray};
143 | }
144 |
145 | .sidenote-number:after,
146 | .sidenote:before {
147 | position: relative;
148 | vertical-align: baseline;
149 | }
150 |
151 | .sidenote-number:after {
152 | content: counter(sidenote-counter);
153 | }
154 |
155 | .sidenote:before {
156 | content: counter(sidenote-counter) ' ';
157 | font-size: 0.9em;
158 | top: -0.3rem;
159 | padding-right: 8px;
160 | }
161 |
162 | blockquote .marginnote {
163 | margin-right: -82%;
164 | min-width: 59%;
165 | text-align: left;
166 | }
167 |
168 | label.sidenote-number {
169 | display: inline;
170 | }
171 |
172 | label.margin-toggle:not(.sidenote-number) {
173 | display: none;
174 | }
175 | input.margin-toggle {
176 | display: none;
177 | }
178 | ${bpMaxLG} {
179 | label.margin-toggle:not(.sidenote-number) {
180 | display: inline;
181 | }
182 |
183 | .sidenote,
184 | .marginnote {
185 | display: none;
186 | }
187 |
188 | .margin-toggle:checked + .marginnote {
189 | display: block;
190 | float: left;
191 | left: 1rem;
192 | clear: both;
193 | width: 95%;
194 | margin: 1rem 2.5%;
195 | vertical-align: baseline;
196 | position: relative;
197 | }
198 | label {
199 | cursor: pointer;
200 | }
201 | }
202 | `
203 |
204 | return (
205 |
206 |
209 | ⊕
210 |
211 |
212 | {children}
213 |
214 | )
215 | }
216 |
217 | export { Footnote, Marginnote }
218 |
--------------------------------------------------------------------------------
/src/components/layout/tippyBox.css:
--------------------------------------------------------------------------------
1 | .tippy-box {
2 | max-height: 500px;
3 | overflow: hidden;
4 | }
5 |
6 | .tippy-box:hover {
7 | overflow-y: auto;
8 | }
9 | .tippy-box:focus {
10 | overflow-y: auto;
11 | }
--------------------------------------------------------------------------------
/src/components/layout/tooltip.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React, { forwardRef } from 'react'
3 | import Tippy from '@tippyjs/react'
4 | import 'tippy.js/dist/tippy.css'
5 | import 'tippy.js/animations/shift-away.css'
6 | import 'tippy.js/themes/light.css'
7 |
8 | import { jsx, css } from '@emotion/react'
9 | import colors from '../../lib/colors'
10 |
11 | const Tooltip = forwardRef((props, ref) => {
12 | let placement
13 | let multiple
14 | if (props.placement) {
15 | placement = props.placement
16 | } else {
17 | placement = 'top'
18 | }
19 | if (props.multiple) {
20 | multiple = props.multiple
21 | } else {
22 | multiple = true
23 | }
24 | return (
25 |
40 |
51 | {props.children}
52 |
53 |
54 | )
55 | })
56 |
57 | export default Tooltip
58 |
--------------------------------------------------------------------------------
/src/components/notes/note-nav.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React from 'react'
3 | import { css, jsx } from '@emotion/react'
4 | import styled from '@emotion/styled'
5 | import { useStaticQuery, graphql, Link } from 'gatsby'
6 | import { InternalLink, InternalNotesLink } from '../layout/links'
7 | import { bpMinXL, bpMaxLG, bpMaxMD, bpMaxXL } from '../../lib/breakpoints'
8 | import colors from '../../lib/colors'
9 |
10 | const Nav = styled.nav`
11 | background: white;
12 | width: 20%;
13 | margin-right: 1.5rem;
14 | margin-top: 1.5rem;
15 |
16 | details summary {
17 | cursor: pointer;
18 | }
19 |
20 | details summary > * {
21 | display: inline;
22 | }
23 | ul ul {
24 | margin-left: 1rem;
25 | }
26 |
27 | ul {
28 | padding: 0;
29 | margin: 0;
30 |
31 | font-size: 1rem;
32 | li {
33 | display: block;
34 | }
35 | }
36 | ${bpMaxMD} {
37 | display: none;
38 | }
39 | `
40 |
41 | const NoteNav = React.memo(() => {
42 | const data = useStaticQuery(graphql`
43 | query {
44 | notes: allMdx(
45 | filter: {
46 | fileAbsolutePath: { regex: "/My-notes/" }
47 | frontmatter: { published: { eq: true } }
48 | }
49 | ) {
50 | nodes {
51 | frontmatter {
52 | id
53 | title
54 | }
55 | slug
56 | }
57 | }
58 | }
59 | `)
60 | // Let's get these values in a form that's easier to manage
61 | let notes = data.notes.nodes.map((note) => {
62 | return {
63 | id: note.frontmatter.id,
64 | title: note.frontmatter.title,
65 | slug: note.slug.substring(note.slug.lastIndexOf('/') + 1),
66 | hierarchies: note.slug
67 | .substring(note.slug.lastIndexOf('/') + 1)
68 | .split('.'),
69 | }
70 | })
71 | //Now let's sort it based on the slug field
72 | notes = notes.sort((a, b) => {
73 | if (a.slug > b.slug) {
74 | return 1
75 | } else if (a.slug < b.slug) {
76 | return -1
77 | }
78 | })
79 |
80 | let topHierarchies = []
81 |
82 | for (let index = 0; index < notes.length; index++) {
83 | //check if we already created a top level hierarchy
84 | if (
85 | !topHierarchies.some(
86 | (hierarchy) => hierarchy.parent.slug === notes[index].hierarchies[0]
87 | )
88 | ) {
89 | //check if the top level note is published (because we sorted the list if it exists it should be first) if not insert a dummy object
90 | if (notes[index].slug === notes[index].hierarchies[0]) {
91 | topHierarchies.push({ parent: notes[index], children: [] })
92 | } else {
93 | topHierarchies.push({
94 | parent: {
95 | slug: notes[index].hierarchies[0],
96 | title: notes[index].hierarchies[0],
97 | id: 404,
98 | },
99 | children: [{ parent: notes[index], children: [] }],
100 | })
101 | }
102 | } else {
103 | let foundIndex1 = topHierarchies.findIndex(
104 | (x) => x.parent.slug === notes[index].hierarchies[0]
105 | )
106 | if (
107 | !topHierarchies[foundIndex1].children.some(
108 | (hierarchy) =>
109 | hierarchy.parent.slug ===
110 | notes[index].hierarchies[0] + '.' + notes[index].hierarchies[1]
111 | )
112 | ) {
113 | //check if the second level note is published (because we sorted the list if it exists it should be first) if not insert a dummy object
114 | if (
115 | notes[index].slug ===
116 | notes[index].hierarchies[0] + '.' + notes[index].hierarchies[1]
117 | ) {
118 | topHierarchies[foundIndex1].children.push({
119 | parent: notes[index],
120 | children: [],
121 | })
122 | } else {
123 | topHierarchies[foundIndex1].children.push({
124 | parent: {
125 | slug:
126 | notes[index].hierarchies[0] + '.' + notes[index].hierarchies[1],
127 | title: notes[index].hierarchies[1],
128 | id: 404,
129 | },
130 | children: [{ parent: notes[index], children: [] }],
131 | })
132 | }
133 | } else {
134 | let foundIndex2 = topHierarchies[foundIndex1].children.findIndex(
135 | (x) =>
136 | x.parent.slug ===
137 | notes[index].hierarchies[0] + '.' + notes[index].hierarchies[1]
138 | )
139 | topHierarchies[foundIndex1].children[foundIndex2].children.push({
140 | parent: notes[index],
141 | children: [],
142 | })
143 | }
144 | }
145 | }
146 |
147 | return (
148 |
149 |
150 |
151 | Table of Notes
152 |
153 | {topHierarchies.map((hierarchy) => {
154 | // non-published first-level hierarchy
155 | if (hierarchy.parent.id === 404) {
156 | return (
157 |
158 | {hierarchy.parent.title}
159 |
160 | {hierarchy.children.map((note) => {
161 | // non-published second-level hierarchy
162 | if (note.parent.id === 404) {
163 | return (
164 |
165 | {note.parent.title}
166 | {note.children.map((note2) => {
167 | //children of non-published second-level hierarchy
168 | return (
169 |
170 |
171 | {note2.parent.title}
172 |
173 |
174 | )
175 | })}
176 |
177 | )
178 | } else {
179 | //published second-level hierarchy
180 | if (note.children.length === 0) {
181 | // second-level hierarchy no children
182 | return (
183 |
184 |
185 | {note.parent.title}
186 |
187 |
188 | )
189 | } else {
190 | //second-level hierarchy w/children
191 |
192 | return (
193 |
194 |
195 |
196 |
197 | {note.parent.title}
198 |
199 |
200 | {note.children.map((note2) => {
201 | return (
202 |
203 |
205 | {note2.parent.title}
206 |
207 |
208 | )
209 | })}
210 |
211 |
212 | )
213 | }
214 | }
215 | })}
216 |
217 |
218 | )
219 | } else {
220 | return (
221 |
222 |
223 |
224 | {hierarchy.parent.title}
225 |
226 |
227 |
228 | {hierarchy.children.map((note) => {
229 | if (note.parent.id === '404') {
230 | return (
231 |
232 | {note.parent.title}
233 |
234 | {note.children.map((note2) => {
235 | return (
236 |
237 |
238 | {note2.parent.title}
239 |
240 |
241 | )
242 | })}
243 |
244 | )
245 | } else if (note.children.length === 0) {
246 | return (
247 |
248 |
249 | {note.parent.title}
250 |
251 |
252 | )
253 | } else {
254 | return (
255 |
256 |
257 |
258 | {note.parent.title}
259 |
260 |
261 | {note.children.map((note2) => {
262 | return (
263 |
264 |
265 | {note2.parent.title}
266 |
267 |
268 | )
269 | })}
270 |
271 | )
272 | }
273 | })}
274 |
275 |
276 | )
277 | }
278 | })}
279 |
280 |
281 | )
282 | })
283 |
284 | export default NoteNav
285 |
--------------------------------------------------------------------------------
/src/components/notes/note.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React from 'react'
3 | import { css, jsx } from '@emotion/react'
4 | import { MDXRenderer } from 'gatsby-plugin-mdx'
5 | import Layout from '../layout/layout'
6 | import { graphql } from 'gatsby'
7 | import LinktipPreview from '../layout/linktip-preview'
8 | import { MDXProvider } from '@mdx-js/react'
9 | import regularComponents from '../layout/mdx-components'
10 | import { InternalLink, InternalNotesLink } from '../layout/links'
11 | import NoteNav from './note-nav'
12 | const NotePage = ({ data, pageContext, location }) => {
13 | //notes that reference this note
14 | let references = []
15 | let referenceBlock
16 | let nodes = data.allMdx.nodes
17 | let relatedPoemsBlock
18 | let relatedArticlesBlock
19 |
20 | let relatedArticles = []
21 | let relatedPoems = []
22 | const popups = {}
23 | nodes.map((post) => {
24 | if (
25 | data.mdx.frontmatter.tags != null &&
26 | post.frontmatter.tags != null &&
27 | post.frontmatter.tags.some((tag) =>
28 | data.mdx.frontmatter.tags.includes(tag)
29 | ) &&
30 | post.frontmatter.slug.includes('/articles/')
31 | ) {
32 | relatedArticles.push(
33 |
34 |
37 | {post.frontmatter.title}
38 | {post.body}
39 |
40 | }
41 | placement="right">
42 |
43 | {post.frontmatter.title}
44 |
45 |
46 |
47 | )
48 | } else if (
49 | data.mdx.frontmatter.tags != null &&
50 | post.frontmatter.tags != null &&
51 | post.frontmatter.tags.some((tag) =>
52 | data.mdx.frontmatter.tags.includes(tag)
53 | ) &&
54 | post.frontmatter.slug.includes('/poetry/')
55 | ) {
56 | relatedPoems.push(
57 |
58 |
61 | {post.frontmatter.title}
62 | {post.body}
63 |
64 | }
65 | placement="right">
66 |
67 | {post.frontmatter.title}
68 |
69 |
70 |
71 | )
72 | }
73 |
74 | if (post) {
75 | popups[`${post.frontmatter.slug}`] = {
76 | title: post.frontmatter.title,
77 | body: post.body,
78 | }
79 | }
80 | })
81 |
82 | if (relatedArticles.length > 0) {
83 | relatedArticlesBlock = (
84 | <>
85 | Related Articles
86 |
87 | >
88 | )
89 | }
90 | if (relatedPoems.length > 0) {
91 | relatedPoemsBlock = (
92 | <>
93 | Related Poems
94 |
95 | >
96 | )
97 | }
98 | if (data.mdx.inboundReferences != null) {
99 | references = data.mdx.inboundReferences.map((ref) =>
100 | ref.frontmatter.published ? (
101 |
102 |
105 | {ref.frontmatter.title}
106 | {ref.body}
107 |
108 | }
109 | placement="right">
110 |
111 | {ref.frontmatter.title}
112 |
113 |
114 |
115 | ) : null
116 | )
117 | }
118 | //related note block
119 | if (references.length > 0 && references[0] != null) {
120 | referenceBlock = (
121 | <>
122 | Related Notes
123 |
124 | >
125 | )
126 | }
127 |
128 | return (
129 |
134 | {data.mdx.body}
135 | {referenceBlock}
136 | {relatedArticlesBlock}
137 | {relatedPoemsBlock}
138 |
139 | )
140 | }
141 |
142 | export const pageQuery = graphql`
143 | query NoteQuery($id: String) {
144 | mdx(id: { eq: $id }) {
145 | inboundReferences {
146 | ... on Mdx {
147 | frontmatter {
148 | id
149 | title
150 | published
151 | }
152 | body
153 | }
154 | }
155 | body
156 | excerpt
157 | frontmatter {
158 | title
159 | id
160 | tags
161 | }
162 | }
163 | allMdx(filter: { fileAbsolutePath: { regex: "/content/" } }) {
164 | nodes {
165 | frontmatter {
166 | tags
167 | slug
168 | title
169 | }
170 | id
171 | body
172 | }
173 | }
174 | }
175 | `
176 |
177 | export default NotePage
178 |
--------------------------------------------------------------------------------
/src/components/poems/poem-layout.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { graphql } from 'gatsby'
3 | import { MDXRenderer } from 'gatsby-plugin-mdx'
4 | import Audio from '../audio'
5 | import { InternalLink } from '../layout/links'
6 |
7 | import Layout from '../layout/layout'
8 | import SimilarContent from '../similar-content'
9 |
10 | export default function PageTemplate({ data: { mdx }, location }) {
11 | let recording = mdx.frontmatter.recording
12 | return (
13 |
18 |
19 | {mdx.body}
20 |
21 | All Poems →
22 |
23 |
27 |
28 | )
29 | }
30 |
31 | export const pageQuery = graphql`
32 | query PoemQuery($id: String) {
33 | mdx(id: { eq: $id }) {
34 | id
35 | body
36 | frontmatter {
37 | tags
38 | title
39 | excerpt
40 | recording
41 | }
42 | }
43 | }
44 | `
45 |
--------------------------------------------------------------------------------
/src/components/poems/poem-list.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from '@emotion/styled'
3 |
4 | import PostLink from '../posts/post-link'
5 |
6 | export const PoemList = styled.ul`
7 | list-style-type: none;
8 | padding: 4px;
9 | margin: 0;
10 | `
11 |
12 | export default ({ posts }) => (
13 |
14 | {posts.map(({ node: post }) => (
15 |
21 | ))}
22 |
23 | )
24 |
--------------------------------------------------------------------------------
/src/components/poems/poem-tag.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { InternalLink, InternalNotesLink } from '../layout/links'
3 | import { graphql, Link } from 'gatsby'
4 | import Layout from '../layout/layout'
5 | import PostList from '../posts/post-list'
6 | import { MDXProvider } from '@mdx-js/react'
7 | import MDXRenderer from 'gatsby-plugin-mdx/mdx-renderer'
8 | import components from '../layout/mdx-components'
9 | import LinktipPreview from '../layout/linktip-preview'
10 |
11 | const PoemTag = ({ data, pageContext, location }) => {
12 | const { tag } = pageContext
13 | const { edges, totalCount } = data.poems
14 | let note = data.notes
15 | if (note != null) {
16 | let references = []
17 | let referenceBlock
18 | if (note.inboundReferenceNotes != null) {
19 | references = note.inboundReferenceNotes.map((ref) => (
20 |
21 |
22 | {ref.title}
23 |
24 |
25 | ))
26 | }
27 |
28 | if (references.length > 0) {
29 | referenceBlock = (
30 | <>
31 | Referenced in
32 |
33 | >
34 | )
35 | }
36 | return (
37 |
42 |
43 |
44 |
48 | {note.title}
49 | {note.childMdx.body}
50 | {referenceBlock}
51 |
52 | }>
53 |
54 | my notes on {tag}
55 |
56 |
57 |
58 |
59 | See all tags →
60 |
61 |
62 | )
63 | } else {
64 | return (
65 |
70 |
71 |
72 |
73 | See all tags →
74 |
75 |
76 | )
77 | }
78 | }
79 |
80 | export default PoemTag
81 |
82 | export const pageQuery = graphql`
83 | query PoemTag($tag: String) {
84 | poems: allMdx(
85 | limit: 2000
86 | sort: { fields: [frontmatter___date], order: DESC }
87 | filter: {
88 | frontmatter: { tags: { in: [$tag] } }
89 | fileAbsolutePath: { regex: "/content/poems/" }
90 | }
91 | ) {
92 | totalCount
93 | edges {
94 | node {
95 | frontmatter {
96 | title
97 | slug
98 | excerpt
99 | tags
100 | }
101 | body
102 | }
103 | }
104 | }
105 | }
106 | `
107 |
--------------------------------------------------------------------------------
/src/components/poems/poem-tags.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import Layout from '../layout/layout'
4 | import TagList from '../tags/list'
5 |
6 | const PoemTags = ({ pageContext, location }) => {
7 | const { tags } = pageContext
8 | return (
9 |
14 |
15 |
16 | )
17 | }
18 |
19 | export default PoemTags
20 |
--------------------------------------------------------------------------------
/src/components/posts/post-link.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { InternalLink } from '../layout/links'
3 | import styled from '@emotion/styled'
4 |
5 | export const PostLink = styled.li`
6 | padding: 0.25rem;
7 | max-width: 400px;
8 | margin: 4px 0;
9 | box-shadow: 0px 0px 1px 1px rgba(0, 0, 0, 0.01);
10 | transition: all 200ms ease-in-out;
11 | border-radius: 5px;
12 | &:hover {
13 | box-shadow: 1px 4px 10px 3px rgba(28, 98, 103, 0.3);
14 | transform: scale(1.02);
15 | }
16 | `
17 |
18 | export default ({ slug, title, body }) => (
19 |
20 | {title}
21 |
22 | )
23 |
--------------------------------------------------------------------------------
/src/components/posts/post-list.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React from 'react'
3 | import styled from '@emotion/styled'
4 | import { jsx, css } from '@emotion/react'
5 | import { InternalLink } from '../layout/links'
6 | import { bpMaxLG, bpMaxSM } from '../../lib/breakpoints'
7 | import colors from '../../lib/colors'
8 | import { Link } from 'gatsby'
9 |
10 | export const PostListStyled = styled.ul`
11 | list-style-type: none;
12 | padding: 4px 0px;
13 | margin: 0;
14 | ${bpMaxLG} {
15 | width: 100%;
16 | }
17 | `
18 |
19 | export const PostLinkStyled = styled.li`
20 | padding: 0.5rem 0.5rem 0.5rem 0;
21 | margin: 0 0rem 0.5rem 0rem;
22 | display: block;
23 | width: fit-content;
24 | height: fit-content;
25 | box-shadow: 0px 0px 1px 1px rgba(0, 0, 0, 0.01);
26 | transition: all 200ms ease-in-out;
27 | border-radius: 0 5px 5px 0;
28 |
29 | &:hover {
30 | box-shadow: 1px 4px 10px 3px rgba(28, 98, 103, 0.3);
31 | transform: scale(1.02);
32 | border-left: 4px solid ${colors.blue};
33 | padding: 0.5rem 0.5rem 0.5rem 0.5rem;
34 | }
35 |
36 | ul {
37 | list-style-type: none;
38 | margin: 0;
39 | padding: 0;
40 | }
41 | li {
42 | display: inline;
43 | word-break: break-word;
44 | margin: 0;
45 | padding: 0;
46 | }
47 | `
48 |
49 | const TagLink = styled(Link)`
50 | font-style: italic;
51 | font-size: 1rem;
52 | text-decoration: none;
53 | color: ${colors.gray};
54 | border-radius: 5px;
55 | padding: 4px 0px;
56 | margin-right: 1rem;
57 | &:hover {
58 | color: ${colors.red};
59 | }
60 | `
61 |
62 | export const Description = styled.div`
63 | font-size: 1.4rem;
64 | padding: 0.5rem 0;
65 | color: ${colors.gray};
66 | `
67 |
68 | export default ({ posts, type }) => {
69 | let color
70 | switch (type) {
71 | case 'article':
72 | color = colors.blue
73 | break
74 | case 'poem':
75 | color = colors.red
76 | break
77 | default:
78 | color = colors.blue
79 | break
80 | }
81 |
82 | return (
83 |
84 | {posts.map(({ node: post }) => (
85 |
92 |
93 | {post.frontmatter.title}
94 |
95 | {post.frontmatter.excerpt}
96 |
97 | {post.frontmatter.tags.map((tag) => (
98 |
99 |
108 | {tag}
109 |
110 |
111 | ))}
112 |
113 |
114 | ))}
115 |
116 | )
117 | }
118 |
--------------------------------------------------------------------------------
/src/components/posts/post-tag.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React from 'react'
3 | import { jsx } from '@emotion/react'
4 | import { graphql } from 'gatsby'
5 | import { InternalLink, InternalNotesLink } from '../layout/links'
6 | import Layout from '../layout/layout'
7 | import PostList from './post-list'
8 | import { MDXProvider } from '@mdx-js/react'
9 | import MDXRenderer from 'gatsby-plugin-mdx/mdx-renderer'
10 | import components from '../layout/mdx-components'
11 | import LinktipPreview from '../layout/linktip-preview'
12 |
13 | const ArticleTag = ({ data, pageContext, location }) => {
14 | const { tag } = pageContext
15 | const { edges, totalCount } = data.articles
16 | let note = data.notes
17 | if (note != null) {
18 | let references = []
19 | let referenceBlock
20 | if (note.inboundReferenceNotes != null) {
21 | references = note.inboundReferenceNotes.map((ref) => (
22 |
23 |
24 | {ref.title}
25 |
26 |
27 | ))
28 | }
29 |
30 | if (references.length > 0) {
31 | referenceBlock = (
32 | <>
33 | Referenced in
34 |
35 | >
36 | )
37 | }
38 | return (
39 |
44 |
45 |
46 |
50 | {note.title}
51 | {note.childMdx.body}
52 | {referenceBlock}
53 |
54 | }>
55 |
56 | my notes on {tag}
57 |
58 |
59 |
60 | See all tags →
61 |
62 | )
63 | } else {
64 | return (
65 |
70 |
71 |
72 | See all tags →
73 |
74 | )
75 | }
76 | }
77 |
78 | export default ArticleTag
79 |
80 | export const pageQuery = graphql`
81 | query ArticleTag($tag: String) {
82 | articles: allMdx(
83 | limit: 2000
84 | sort: { fields: [frontmatter___date], order: DESC }
85 | filter: {
86 | frontmatter: { tags: { in: [$tag] } }
87 | fileAbsolutePath: { regex: "/content/posts/" }
88 | }
89 | ) {
90 | totalCount
91 | edges {
92 | node {
93 | frontmatter {
94 | title
95 | slug
96 | excerpt
97 | tags
98 | }
99 | body
100 | }
101 | }
102 | }
103 | }
104 | `
105 |
--------------------------------------------------------------------------------
/src/components/posts/post-tags.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import Layout from '../layout/layout'
4 | import TagList from '../tags/list'
5 |
6 | const ArticleTags = ({ pageContext, location }) => {
7 | const { tags } = pageContext
8 | return (
9 |
14 |
15 |
16 | )
17 | }
18 |
19 | export default ArticleTags
20 |
--------------------------------------------------------------------------------
/src/components/posts/post.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React from 'react'
3 | import { css, jsx } from '@emotion/react'
4 | import styled from '@emotion/styled'
5 | import { MDXRenderer } from 'gatsby-plugin-mdx'
6 | import Tooltip from '../layout/tooltip'
7 | import Layout from '../layout/layout'
8 | import { graphql } from 'gatsby'
9 | import SimilarContent from '../similar-content'
10 | import colors from '../../lib/colors'
11 |
12 | const WordCount = styled.div`
13 | position: absolute;
14 | bottom: 0rem;
15 | `
16 |
17 | const Post = ({ data, pageContext, location }) => {
18 | const wordCount = data.mdx.wordCount.words
19 | let emoji
20 | if (wordCount < 500) {
21 | emoji = '📄'
22 | } else if (wordCount < 1000) {
23 | emoji = '📄📄'
24 | } else if (wordCount < 1500) {
25 | emoji = '📄📄📄'
26 | } else if (wordCount < 2000) {
27 | emoji = '📄📄📄📄'
28 | } else if (wordCount < 2500) {
29 | emoji = '📄📄📄📄📄'
30 | } else {
31 | emoji = '📄📄📄📄📄📄'
32 | }
33 |
34 | return (
35 |
41 |
46 | {data.mdx.frontmatter.title}
47 | {emoji}
48 |
49 |
57 | {' '}
58 | First published on{' '}
59 |
60 | {new Date(data.mdx.frontmatter.date).toLocaleDateString(undefined, {
61 | year: 'numeric',
62 | month: 'long',
63 | day: 'numeric',
64 | })}
65 |
66 | .
67 |
68 | {data.mdx.body}
69 |
73 |
74 | )
75 | }
76 |
77 | export const pageQuery = graphql`
78 | query PostQuery($id: String) {
79 | mdx(id: { eq: $id }) {
80 | id
81 | body
82 | frontmatter {
83 | title
84 | excerpt
85 | tags
86 | date
87 | }
88 | timeToRead
89 | wordCount {
90 | words
91 | }
92 | }
93 | }
94 | `
95 | export default Post
96 |
--------------------------------------------------------------------------------
/src/components/prism.css:
--------------------------------------------------------------------------------
1 | /**
2 | * prism.js default theme for JavaScript, CSS and HTML
3 | * Based on dabblet (http://dabblet.com)
4 | * @author Lea Verou
5 | */
6 | @import url('https://fonts.googleapis.com/css2?family=Fira+Code&display=swap');
7 |
8 | code[class*='language-'],
9 | pre[class*='language-'] {
10 | color: black;
11 | background: none;
12 | text-shadow: 0 1px white;
13 | font-family: 'Fira Code', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono',
14 | monospace;
15 | font-weight: 400;
16 | font-size: 16px;
17 | text-align: left;
18 | white-space: pre;
19 | word-spacing: normal;
20 | word-break: normal;
21 | word-wrap: normal;
22 | line-height: 1.5;
23 |
24 | -moz-tab-size: 4;
25 | -o-tab-size: 4;
26 | tab-size: 4;
27 |
28 | -webkit-hyphens: none;
29 | -moz-hyphens: none;
30 | -ms-hyphens: none;
31 | hyphens: none;
32 | width: 52.5%;
33 | }
34 |
35 | pre[class*='language-']::-moz-selection,
36 | pre[class*='language-'] ::-moz-selection,
37 | code[class*='language-']::-moz-selection,
38 | code[class*='language-'] ::-moz-selection {
39 | text-shadow: none;
40 | color: '#ffffff';
41 | background-color: '#75B9BE';
42 | height: '100%';
43 | text-shadow: none;
44 | }
45 |
46 | pre[class*='language-']::selection,
47 | pre[class*='language-'] ::selection,
48 | code[class*='language-']::selection,
49 | code[class*='language-'] ::selection {
50 | color: '#ffffff';
51 | background-color: '#75B9BE';
52 | height: '100%';
53 | text-shadow: none;
54 | }
55 |
56 | @media (max-width: 1199px) {
57 | code[class*='language-'],
58 | pre[class*='language-'] {
59 | width: 97%;
60 | }
61 | }
62 |
63 | @media print {
64 | code[class*='language-'],
65 | pre[class*='language-'] {
66 | text-shadow: none;
67 | }
68 | }
69 |
70 | /* Code blocks */
71 | pre[class*='language-'] {
72 | padding: 1em;
73 | margin: 0.5em 0;
74 | overflow: auto;
75 | }
76 |
77 | :not(pre) > code[class*='language-'],
78 | pre[class*='language-'] {
79 | background: #eff4f5;
80 | }
81 |
82 | /* Inline code */
83 | :not(pre) > code[class*='language-'] {
84 | padding: 0.1em;
85 | border-radius: 0.3em;
86 | white-space: normal;
87 | }
88 |
89 | .token.comment,
90 | .token.prolog,
91 | .token.doctype,
92 | .token.cdata {
93 | color: slategray;
94 | }
95 |
96 | .token.punctuation {
97 | color: #999;
98 | }
99 |
100 | .namespace {
101 | opacity: 0.7;
102 | }
103 |
104 | .token.property,
105 | .token.tag,
106 | .token.boolean,
107 | .token.number,
108 | .token.constant,
109 | .token.symbol,
110 | .token.deleted {
111 | color: #805168;
112 | }
113 |
114 | .token.selector,
115 | .token.attr-name,
116 | .token.string,
117 | .token.char,
118 | .token.builtin,
119 | .token.inserted {
120 | color: #406669;
121 | }
122 |
123 | .token.operator,
124 | .token.entity,
125 | .token.url,
126 | .language-css .token.string,
127 | .style .token.string {
128 | color: #a67f59;
129 | background: hsla(0, 0%, 100%, 0.5);
130 | }
131 |
132 | .token.atrule,
133 | .token.attr-value,
134 | .token.keyword {
135 | color: #4d6872;
136 | }
137 |
138 | .token.function {
139 | color: #dd4a68;
140 | }
141 |
142 | .token.regex,
143 | .token.important,
144 | .token.variable {
145 | color: #e90;
146 | }
147 |
148 | .token.important,
149 | .token.bold {
150 | font-weight: bold;
151 | }
152 | .token.italic {
153 | font-style: italic;
154 | }
155 |
156 | .token.entity {
157 | cursor: help;
158 | }
159 |
--------------------------------------------------------------------------------
/src/components/seo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Helmet } from 'react-helmet'
3 | import { useStaticQuery, graphql } from 'gatsby'
4 |
5 | export default ({
6 | title,
7 | description,
8 | seoTitle,
9 | seoTitleAddition1,
10 | seoTitleAddition2,
11 | type,
12 | location,
13 | }) => {
14 | const {
15 | site: { siteMetadata },
16 | } = useStaticQuery(graphql`
17 | {
18 | site {
19 | siteMetadata {
20 | title
21 | seoTitleAddition1
22 | seoTitleAddition2
23 | description
24 | twitter
25 | siteUrl
26 | }
27 | }
28 | }
29 | `)
30 | const siteUrl = siteMetadata.siteUrl
31 |
32 | const pageTitle = title || seoTitle || siteMetadata.title
33 |
34 | const seoLongTitle = `${title || siteMetadata.seoTitleAddition1} `
35 | const seoDescription = description || siteMetadata.description
36 | const author = 'D.S. Chapman'
37 |
38 | const url = `${siteUrl}${location.pathname}`
39 | const ogImage = `https://dschapman-functions.netlify.app/opengraph?title=${pageTitle}&author=${author}&type=${type}&v=1.0.1`
40 | return (
41 |
42 | {seoLongTitle}
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
66 |
67 | )
68 | }
69 |
--------------------------------------------------------------------------------
/src/components/similar-content.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React from 'react'
3 | import { useStaticQuery, graphql } from 'gatsby'
4 | import { PostLinkStyled, Description, PostListStyled } from './posts/post-list'
5 | import { jsx, css } from '@emotion/react'
6 | import colors from './../lib/colors'
7 | import { InternalLink } from './layout/links'
8 |
9 | export const useContentMdx = () => {
10 | return useStaticQuery(graphql`
11 | query ContentMdx {
12 | content: allMdx(
13 | filter: {
14 | fileAbsolutePath: { regex: "/dschapman-com-content/[poems|posts]/" }
15 | }
16 | ) {
17 | nodes {
18 | frontmatter {
19 | slug
20 | title
21 | excerpt
22 | published
23 | tags
24 | featured
25 | }
26 | slug
27 | }
28 | }
29 | blog: allMdx(
30 | filter: { fileAbsolutePath: { regex: "/dschapman-com-content/blog/" } }
31 | ) {
32 | nodes {
33 | excerpt
34 | slug
35 | frontmatter {
36 | date
37 | title
38 | published
39 | tags
40 | featured
41 | }
42 | }
43 | }
44 | }
45 | `)
46 | }
47 |
48 | export default ({ type, tags, title }) => {
49 | let color = colors.blue
50 | const data = useContentMdx()
51 | let matches = {}
52 | tags.filter((tag) =>
53 | data.content.nodes.forEach((node) => {
54 | if (node.frontmatter.tags !== null) {
55 | if (node.frontmatter.tags.indexOf(tag) != -1) {
56 | if (node.frontmatter.title in matches) {
57 | matches[node.frontmatter.title].points++
58 | matches[node.frontmatter.title].tags.push(tag)
59 | } else if (node.frontmatter.title != title) {
60 | if (node.frontmatter.featured == true) {
61 | matches[node.frontmatter.title] = {
62 | points: 2,
63 | slug: node.frontmatter.slug,
64 | tags: [tag],
65 | title: node.frontmatter.title,
66 | description: node.frontmatter.excerpt,
67 | }
68 | } else {
69 | matches[node.frontmatter.title] = {
70 | points: 1,
71 | slug: node.frontmatter.slug,
72 | tags: [tag],
73 | title: node.frontmatter.title,
74 | description: node.frontmatter.excerpt,
75 | }
76 | }
77 | }
78 | }
79 | }
80 | })
81 | )
82 |
83 | tags.filter((tag) =>
84 | data.blog.nodes.forEach((node) => {
85 | if (node.frontmatter.tags !== null) {
86 | if (node.frontmatter.tags.indexOf(tag) != -1) {
87 | if (node.frontmatter.title in matches) {
88 | matches[node.frontmatter.title].points++
89 | matches[node.frontmatter.title].tags.push(tag)
90 | } else if (node.frontmatter.title != title) {
91 | if (node.frontmatter.featured == true) {
92 | matches[node.frontmatter.title] = {
93 | points: 2,
94 | slug: node.slug,
95 | tags: [tag],
96 | title: node.frontmatter.title,
97 | description: node.excerpt,
98 | }
99 | } else {
100 | matches[node.frontmatter.title] = {
101 | points: 1,
102 | slug: `/blog/${node.frontmatter.date.substring(
103 | 0,
104 | 4
105 | )}/${node.frontmatter.date.substring(5, 7)}/${node.slug}`,
106 | tags: [tag],
107 | title: node.frontmatter.title,
108 | description: node.excerpt,
109 | }
110 | }
111 | }
112 | }
113 | }
114 | })
115 | )
116 |
117 | let matchesArray = Object.values(matches)
118 | matchesArray.sort((a, b) => (a.points < b.points ? 1 : -1))
119 | matchesArray = matchesArray.slice(0, 3)
120 | if (matchesArray.length > 0) {
121 | return (
122 | <>
123 | Other things to read
124 |
125 | {matchesArray.map((match) => {
126 | return (
127 |
134 | {match.title}
135 | {match.description}
136 |
137 | )
138 | })}
139 |
140 | >
141 | )
142 | } else {
143 | return <>>
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/components/tags/list-item.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import blogList from '../blog/blog-list'
3 | import { InternalLink } from '../layout/links'
4 | import { PostListStyled } from '../posts/post-list'
5 |
6 | export default ({ tag, tagCount, type }) => {
7 | let slug = ''
8 | const tagSlug = tag
9 | .toString()
10 | .toLowerCase()
11 | .replace(/\s+/g, '-') // Replace spaces with -
12 | .replace(/[^\w\-]+/g, '') // Remove all non-word chars
13 | .replace(/\-\-+/g, '-') // Replace multiple - with single -
14 | .replace(/^-+/, '') // Trim - from start of text
15 | .replace(/-+$/, '') // Trim - from end of text
16 | switch (type) {
17 | case 'all':
18 | slug = '/tag/' + tagSlug
19 | break
20 | case 'poem':
21 | slug = '/poetry/tag/' + tagSlug
22 | break
23 | case 'article':
24 | slug = '/articles/tag/' + tagSlug
25 | break
26 | case 'blog':
27 | slug = '/blog/tag/' + tagSlug
28 | default:
29 | new Error('Unexpected type in Tag List Item')
30 | }
31 |
32 | return (
33 |
34 |
35 | {tag} ({tagCount})
36 |
37 |
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/tags/list.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { PostListStyled } from '../posts/post-list'
3 | import TagListItem from './list-item'
4 |
5 | export default ({ tags, type }) => {
6 | let newTags = tags.filter((tag) => tag.totalCount > 1) //filter out any tags with only one item
7 | return (
8 |
9 | {newTags.map((tag) => (
10 |
16 | ))}
17 |
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/tags/tag.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { InternalLink, InternalNotesLink } from '../layout/links'
3 | import { graphql, Link } from 'gatsby'
4 | import Layout from '../layout/layout'
5 | import PostList from '../posts/post-list'
6 | import BlogList from '../blog/blog-list'
7 | import { MDXProvider } from '@mdx-js/react'
8 | import MDXRenderer from 'gatsby-plugin-mdx/mdx-renderer'
9 | import components from '../layout/mdx-components'
10 | import LinktipPreview from '../layout/linktip-preview'
11 |
12 | const Tag = ({ data, pageContext, location }) => {
13 | const { tag } = pageContext
14 | const poem = data.poems
15 | const article = data.articles
16 | let note = data.notes
17 | let blog = data.blogs
18 |
19 | function PoemBlock() {
20 | if (poem != null) {
21 | if (poem.totalCount > 0) {
22 | return (
23 | <>
24 | Poems
25 |
26 | >
27 | )
28 | } else {
29 | return <>>
30 | }
31 | } else {
32 | return <>>
33 | }
34 | }
35 | function ArticleBlock() {
36 | if (article != null) {
37 | if (article.totalCount > 0) {
38 | return (
39 | <>
40 | Articles
41 |
42 | >
43 | )
44 | } else {
45 | return <>>
46 | }
47 | } else {
48 | return <>>
49 | }
50 | }
51 | function BlogBlock() {
52 | if (blog != null) {
53 | if (blog.totalCount > 0) {
54 | return (
55 | <>
56 | Blogs
57 |
58 | >
59 | )
60 | } else {
61 | return <>>
62 | }
63 | } else {
64 | return <>>
65 | }
66 | }
67 | function NoteBlock() {
68 | if (note != null) {
69 | if (note.totalCount > 0) {
70 | let relatedNotes = note.edges.map(({ node }) => {
71 | return (
72 |
73 |
77 | {node.frontmatter.title}
78 | {node.body}
79 |
80 | }>
81 |
84 | {node.frontmatter.title}
85 |
86 |
87 |
88 | )
89 | })
90 | return (
91 | <>
92 | Notes
93 |
94 | >
95 | )
96 | } else {
97 | return <>>
98 | }
99 | } else {
100 | return <>>
101 | }
102 | }
103 | return (
104 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | See all tags →
117 |
118 |
119 | )
120 | }
121 |
122 | export default Tag
123 |
124 | export const pageQuery = graphql`
125 | query Tag($tag: String) {
126 | poems: allMdx(
127 | limit: 2000
128 | sort: { fields: [frontmatter___date], order: DESC }
129 | filter: {
130 | frontmatter: { tags: { in: [$tag] }, published: { eq: true } }
131 | fileAbsolutePath: { regex: "/content/poems/" }
132 | }
133 | ) {
134 | totalCount
135 | edges {
136 | node {
137 | frontmatter {
138 | title
139 | slug
140 | excerpt
141 | tags
142 | }
143 | body
144 | }
145 | }
146 | }
147 | articles: allMdx(
148 | limit: 2000
149 | sort: { fields: [frontmatter___date], order: DESC }
150 | filter: {
151 | frontmatter: { tags: { in: [$tag] }, published: { eq: true } }
152 | fileAbsolutePath: { regex: "/content/posts/" }
153 | }
154 | ) {
155 | totalCount
156 | edges {
157 | node {
158 | frontmatter {
159 | title
160 | slug
161 | excerpt
162 | tags
163 | }
164 | body
165 | }
166 | }
167 | }
168 | blogs: allMdx(
169 | limit: 2000
170 | sort: { fields: [frontmatter___date], order: DESC }
171 | filter: {
172 | frontmatter: { tags: { in: [$tag] }, published: { eq: true } }
173 | fileAbsolutePath: { regex: "/content/blog/" }
174 | }
175 | ) {
176 | totalCount
177 | edges {
178 | node {
179 | slug
180 | excerpt
181 | frontmatter {
182 | title
183 | date
184 | tags
185 | }
186 | body
187 | }
188 | }
189 | }
190 | notes: allMdx(
191 | filter: {
192 | frontmatter: { tags: { in: [$tag] }, published: { eq: true } }
193 | fileAbsolutePath: { regex: "/My-notes/" }
194 | }
195 | ) {
196 | totalCount
197 | edges {
198 | node {
199 | frontmatter {
200 | title
201 | id
202 | excerpt
203 | tags
204 | }
205 | body
206 | }
207 | }
208 | }
209 | }
210 | `
211 |
--------------------------------------------------------------------------------
/src/components/tags/tags.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import Layout from '../layout/layout'
4 | import TagList from './list'
5 |
6 | const AllTags = ({ pageContext, location }) => {
7 | const { tags } = pageContext
8 | return (
9 |
14 |
15 |
16 | )
17 | }
18 |
19 | export default AllTags
20 |
--------------------------------------------------------------------------------
/src/content/canon.yml:
--------------------------------------------------------------------------------
1 | - 'Poetic Words':
2 | - title: 'Four Quartets'
3 | author: 'T.S. Eliot'
4 | medium: 'poem'
5 | text: "Language and pure meaning interwoven as only T.S. Eliot can - incomprehensible in its clarity, as poignant when you don't understand it as when you think you do."
6 | link: 'www.goodreads.com/book/show/80410.Four_Quartets'
7 | - title: 'Farther Along'
8 | author: 'Josh Garrels'
9 | medium: 'song'
10 | text: "There's so much more to life than we've been told /
11 | It's full of beauty that will unfold /
12 | And shine like you struck gold my wayward son /
13 | That deadweight burden weighs a ton /
14 | Go down into the river and let it run /
15 | And wash away all the things you've done /
16 | Forgiveness alright."
17 | link: 'https://open.spotify.com/album/3jrVFS6lW7HvxOKN7QPQC8'
18 | - title: 'Blessings (ii)'
19 | author: 'Chance the Rapper'
20 | medium: 'song'
21 | text: "I speak of promised lands /
22 | Soil as soft as momma's hands /
23 | Running water, standing still /
24 | Endless fields of daffodils and chamomile ...
25 | I speak to God in public, I speak to God in public /
26 | He keep my rhymes in couplets"
27 | link: 'https://open.spotify.com/track/5IdQEHgtmj9th3OkfQKhf8?si=IJl1ndjMTKCawYIr1AeJ_Q'
28 | - title: 'As Kingfishers Catch Fire'
29 | author: 'Gerard Manley Hopkins'
30 | medium: 'poem'
31 | text: "No one uses language as beautifully as Hopkins. His images well inside of you - beautiful but hard to reconcile with each other until they crystalize around some truth he's planted at the end of the poem."
32 | link: 'https://www.poetryfoundation.org/poems/44389/as-kingfishers-catch-fire'
33 | - 'Fantasy':
34 | - title: 'The Lord of the Rings'
35 | author: 'J.R.R. Tolkien'
36 | medium: 'book'
37 | text: "The first book to teach me that there can be more to great stories than fun plot. Prose that demands to be read aloud, a world that lives on beyond the page, and a vision of the good in the world that's worth fighting for."
38 | link: 'https://www.goodreads.com/book/show/33.The_Lord_of_the_Rings'
39 | - title: 'The Wheel of Time'
40 | author: 'Robert Jordan'
41 | medium: 'book'
42 | text: 'This series is monolithic in many ways. One story slowly unveiled across 14 books, perhaps too slowly at times. Truly a masterclass in building a world and story together.'
43 | link: 'https://www.goodreads.com/series/41526-the-wheel-of-time'
44 | - title: 'The Dark is Rising'
45 | author: 'Susan Cooper'
46 | medium: 'book'
47 | text: '"When the dark comes rising, six shall turn it back, three from the circle, three from the track..." A world that unfurled legend and wonder as it was read aloud to me.'
48 | link: 'https://www.goodreads.com/book/show/210329.The_Dark_Is_Rising'
49 | - 'Science Fiction':
50 | - title: 'Star Trek: Deep Space Nine'
51 | author: 'Rick Berman & Michael Piller'
52 | medium: 'tv'
53 | text: 'A vibrant look at the frontier of space. Darker than other Star Trek series, but also full of life, complex heroes, and likable rogues. The first vision of the future that truly captured my imagination.'
54 | link: 'https://www.imdb.com/title/tt0106145/'
55 |
--------------------------------------------------------------------------------
/src/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dschapman/dschapman-com/7b8c29a7f8afc3c7672b836460404aa34b35b932/src/favicon.png
--------------------------------------------------------------------------------
/src/lib/breakpoints.js:
--------------------------------------------------------------------------------
1 | export const minSM = 545
2 | export const minMD = 768
3 | export const minLG = 998
4 | export const minXL = 1200
5 | export const minXXL = 1920
6 |
7 | export const bpMinSM = `@media (min-width: ${minSM}px)`
8 | export const bpMinMD = `@media (min-width: ${minMD}px)`
9 | export const bpMinLG = `@media (min-width: ${minLG}px)`
10 | export const bpMinXL = `@media (min-width: ${minXL}px)`
11 | export const bpMinXXL = `@media (min-width: ${minXXL}px)`
12 |
13 | export const bpMaxXS = `@media (max-width: ${minSM - 1}px)`
14 | export const bpMaxSM = `@media (max-width: ${minMD - 1}px)`
15 | export const bpMaxMD = `@media (max-width: ${minLG - 1}px)`
16 | export const bpMaxLG = `@media (max-width: ${minXL - 1}px)`
17 | export const bpMaxXL = `@media (max-width: ${minXXL - 1}px)`
18 |
--------------------------------------------------------------------------------
/src/lib/colors.js:
--------------------------------------------------------------------------------
1 | const colors = {
2 | blue: '#7297A6',
3 | lightblue: '#BFE4F2',
4 | hcbluegreen: '#1c6267',
5 | bluegreen: '#75B9BE',
6 | red: '#925C77',
7 | text: '#0a0a0a',
8 | background: '#fff',
9 | primary: '#272727',
10 | gray: '#626262',
11 | }
12 |
13 | export default colors
14 |
--------------------------------------------------------------------------------
/src/lib/dendron-parse-url.js:
--------------------------------------------------------------------------------
1 | const slugify = require('slugify')
2 | module.exports = (title) => {
3 | const segments = title.split('|')
4 | obsidianTitle = segments.shift()
5 | return `/notes/${slugify(obsidianTitle)}`
6 | }
7 |
--------------------------------------------------------------------------------
/src/pages/about.mdx:
--------------------------------------------------------------------------------
1 | import Layout from '../components/layout/layout'
2 | import Avatar from '../components/avatar'
3 | export default (props, location) => (
4 |
11 | )
12 |
13 | # About Me
14 |
15 |
16 |
17 | My name is Daniel Chapman and I am a writer, poet, and all around technology enthusiast
18 | raised in the Pacific Northwest and living in Virginia. I
19 | am passionate about making both poetry and technology more accessible to those outside
20 | those fields.
21 |
22 | Explore this site for more information about me and my eccentric variety of projectsEccentric feels like a strong word, but one of my projects is learning the 1976 text editor - [[tool.emacs|Emacs]] , including my poetry collection — [Seasons of Thought](/poetry/seasons-of-thought) and my current tech projects.
23 |
24 | I love learning and as a firm believer in the liberal arts, I don't think learning should be segmented, so here you'll see my interests represented -- literature, poetry, web development, coding, and whatever else I decide to put on here. Enjoy, I certainly enjoyed making it!
25 |
26 | ## Follow Me on Social Media
27 |
28 | - [Twitter](https://twitter.com/ds_chapman)
29 | - [Writing Instagram](https://instagram.com/dschapmanbooks)
30 | - [My Web Portfolio](https://danielchapman.dev)
31 | - [Photo Instagram](https://instagram.com/dschapmanphotos)
32 | - [Facebook](https://facebook.com/dschapmanbooks)
33 | - [Goodreads](https://www.goodreads.com/author/show/19228078.D_S_Chapman)
34 | - [GitHub](https://github.com/dschapman)
35 |
36 | # About This Website
37 |
38 | This website currently has three different types of content -- [articles](/articles), [poems](/poetry), and [notes](/notes) -- but the unifying metaphor is one of a digital garden, a place where content grows and develops over time, is interconnected and where content is presented in organic as well as manicured ways.For more on digital gardens [[web.digital-garden|check out my note]].
39 |
40 | In order to help distinguish the different types of links on this website I have styled links in three different ways. [Internal links](/) are underlined in blue, while [links to my notes](/notes) are highlighted, and [external links](https://wikipedia.org) are distinguished by red underlines.
41 |
42 | I hope you enjoy exploring this website. If you're curious about how this website was built make sure to check out [how I coded this website](/articles/how-i-coded-this-website) or [the source code](https://github.com/dschapman/PersonalBlog).
43 |
--------------------------------------------------------------------------------
/src/pages/articles/all.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { graphql } from 'gatsby'
3 | import Layout from '../../components/layout/layout'
4 | import PostList from '../../components/posts/post-list'
5 | import { InternalLink } from '../../components/layout/links'
6 |
7 | const PostsIndex = ({ data, location }) => (
8 |
14 | All the articles on my website sorted by title.
15 |
16 |
17 | Return to{' '}
18 |
19 | a curated selection of articles →
20 |
21 |
22 |
23 | )
24 | export const pageQuery = graphql`
25 | query PostsQuery {
26 | allMdx(
27 | filter: { fileAbsolutePath: { regex: "/content/posts/" } }
28 | sort: { fields: frontmatter___title, order: ASC }
29 | limit: 1000
30 | ) {
31 | edges {
32 | node {
33 | id
34 | frontmatter {
35 | excerpt
36 | slug
37 | title
38 | date(formatString: "MMMM DD, YYYY")
39 | tags
40 | }
41 | body
42 | }
43 | }
44 | }
45 | }
46 | `
47 | export default PostsIndex
48 |
--------------------------------------------------------------------------------
/src/pages/articles/index.mdx:
--------------------------------------------------------------------------------
1 | import Layout from '../../components/layout/layout'
2 | import LatestArticle from '../../components/latest-articles'
3 | export default (props, location) => (
4 |
11 | )
12 |
13 | In my writing I like to explore art, writing, technology, and the areas where any of my interests intersect.
14 |
15 | ## Latest Articles
16 |
17 | Here are the latest articles I've published on my website.
18 |
19 |
20 |
21 | [See all posts →](/articles/all)
22 | [See all tags →](/articles/tags)
23 |
--------------------------------------------------------------------------------
/src/pages/blog/index.mdx:
--------------------------------------------------------------------------------
1 | import Layout from '../../components/layout/layout'
2 | import BlogIndex from '../../components/blog/blog-index'
3 | export default (props, location) => (
4 |
12 | )
13 |
14 | As my website has grown I found myself wanting a space for long-form writing that is not as structured or focused on one particular idea or topic - a place for personal musings and reflections to grow and flourish. In otherwords I was looking for a good old fashioned blog.
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/pages/canon.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { React, useState } from 'react'
3 |
4 | import styled from '@emotion/styled'
5 | import { jsx, css } from '@emotion/react'
6 | import data from '../content/canon.yml'
7 | import Layout from '../components/layout/layout.js'
8 | import colors from '../lib/colors'
9 | import { bpMaxSM, bpMaxLG } from '../lib/breakpoints'
10 | import { map } from 'lodash'
11 |
12 | function Canon(props) {
13 | const Container = styled.div(
14 | `
15 | display: flex;
16 | flex-wrap: wrap;
17 | justify-content: space-around;
18 | `
19 | )
20 |
21 | const Category = styled.h2(
22 | `
23 | display: flex;
24 | width: fit-content;
25 | margin-bottom: 2rem;
26 |
27 | `
28 | )
29 |
30 | let categories = data
31 |
32 | return (
33 |
34 |
35 | These are the things that influence how I think, work, and imagine. This
36 | list is far from exhaustive, but tries to identify some of my influences
37 | in my poetry, storytelling, and thought.
38 |
39 |
40 |
41 | {categories.map((list) => {
42 | return (
43 |
44 | {Object.keys(list).pop()}
45 | {Object.values(list).map((item) =>
46 | item.map((node) => {
47 | return (
48 |
56 | )
57 | })
58 | )}
59 |
60 | )
61 | })}
62 |
63 |
64 |
65 | This page was inspired by{' '}
66 |
67 | this blog post
68 | {' '}
69 | by Brendan Schlagel. I borrowed heavily from{' '}
70 |
71 | his source code
72 | {' '}
73 | in creating this page.
74 |
75 |
76 |
77 | If you re interested in the books I ve been reading recently,
78 | check out my{' '}
79 |
80 | Goodreads page
81 |
82 |
83 |
84 | )
85 | }
86 |
87 | function CanonItem(props) {
88 | const { medium, title, text, category, link, author } = props
89 | let icon
90 | switch (medium) {
91 | case 'book':
92 | icon = '📖'
93 | break
94 | case 'song':
95 | icon = '🎵'
96 | break
97 | case 'movie':
98 | icon = '🎥'
99 | break
100 | case 'tv':
101 | icon = '📺'
102 | break
103 | case 'poem':
104 | icon = '📝'
105 | break
106 | default:
107 | icon = '⁉️'
108 | break
109 | }
110 |
111 | const Item = styled.div(
112 | `
113 | display: block;
114 | float: left;
115 | text-align: left;
116 | box-sizing: border-box;
117 | margin: 0 3% 20px 0;
118 | overflow: hidden;
119 | position: relative;
120 | border-radius: 5px;
121 | transition: all .25s;
122 | width: 30%;
123 | min-width: 300px;
124 | min-height:300px;
125 | &: hover {
126 | box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
127 | };
128 | padding-top:1rem;
129 |
130 | ${bpMaxLG} {
131 | width: 40%;
132 | }
133 | ${bpMaxSM} {
134 | width: 100%;
135 | }
136 | `
137 | )
138 |
139 | const Icon = styled.div(
140 | `
141 | font-size:2em;
142 | text-align: center;
143 | `
144 | )
145 |
146 | const Title = styled.h3(
147 | `
148 | text-align: center;
149 | margin-bottom: .25rem;
150 | `
151 | )
152 |
153 | const Author = styled.h4(
154 | `
155 | padding:0;
156 | margin:0;
157 | text-align:center;
158 | `
159 | )
160 |
161 | const Description = styled.p(
162 | `
163 | padding: 15px 20px;
164 | text-align: center;
165 | `
166 | )
167 |
168 | const ItemLink = styled.a(
169 | `
170 | text-decoration:none;
171 | color: ${colors.text};
172 | &:hover {
173 | color: ${colors.text};
174 | text-decoration:none;
175 | opacity: .8;
176 | -webkit-transition: all -webkit-transform .25s;
177 | transition: all .25s;
178 | };
179 | `
180 | )
181 |
182 | return (
183 | -
184 |
185 | {icon}
186 | {title}
187 | {author}
188 | {text}
189 |
190 |
191 | )
192 | }
193 |
194 | function SortTitle(a, b) {
195 | let titleA = a.title.toUpperCase() // ignore upper and lowercase
196 | let titleB = b.title.toUpperCase() // ignore upper and lowercase
197 | if (titleA < titleB) {
198 | return -1
199 | }
200 | if (titleA > titleB) {
201 | return 1
202 | }
203 |
204 | // names must be equal
205 | return 0
206 | }
207 |
208 | function SortAuthor(a, b) {
209 | let authorA = a.author.toUpperCase() // ignore upper and lowercase
210 | let authorB = b.author.toUpperCase() // ignore upper and lowercase
211 | if (authorA < authorB) {
212 | return -1
213 | }
214 | if (authorA > authorB) {
215 | return 1
216 | }
217 |
218 | // names must be equal
219 | return 0
220 | }
221 |
222 | function SortMedium(a, b) {
223 | let A = a.medium.toUpperCase() // ignore upper and lowercase
224 | let B = b.medium.toUpperCase() // ignore upper and lowercase
225 | if (A < B) {
226 | return -1
227 | }
228 | if (A > B) {
229 | return 1
230 | }
231 |
232 | // names must be equal
233 | return 0
234 | }
235 |
236 | const CanonPage = ({ data, location, props }) => {
237 | return (
238 |
244 |
245 |
246 | )
247 | }
248 |
249 | export default CanonPage
250 |
--------------------------------------------------------------------------------
/src/pages/index.mdx:
--------------------------------------------------------------------------------
1 | import Layout from '../components/layout/layout'
2 | import Featured from '../components/featured'
3 | export default (props, { location }) => (
4 |
11 | )
12 |
13 | # I'm Daniel -- Poet, Writer, Tinkerer
14 |
15 | This site is a [[web.digital-garden|digital garden]] -- an organic space that is meant to be explored as I work and tend it.
16 |
17 | Here you'll find longer form [articles](/articles), [poems](/poetry), raw [notes](/notes), and the occasional [blog](/blog) post.
18 |
19 | ---
20 |
21 | - [About Me](/about)
22 | - [My book of poetry](/poetry/seasons-of-thought)
23 | - [My Web Development Portfolio](https://danielchapman.dev)
24 | - [[me.toolbox|My Toolbox]]
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/pages/mileage-metrics.mdx:
--------------------------------------------------------------------------------
1 | import Layout from '../components/layout/layout'
2 | export default (props, location) => (
3 |
10 | )
11 |
12 | # Mileage Metrics
13 | Summarize your HealthKit running workouts and get stats for your overall mileage for the year, the week, or all time. Add a widget and stay motivated as you see your total grow. Stay on top of your current training load with a graph of your running mileage over the last 4 weeks.
14 |
15 | ## Support
16 | For assistance with Mileage Metrics, contact help(at)dschapman.com.
17 |
18 |
--------------------------------------------------------------------------------
/src/pages/notes/404.mdx:
--------------------------------------------------------------------------------
1 | import Layout from '../../components/layout/layout'
2 | export default (props, location) => (
3 |
10 | )
11 |
12 | The note you were looking for has not been published yet.
13 |
--------------------------------------------------------------------------------
/src/pages/notes/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { MDXRenderer } from 'gatsby-plugin-mdx'
3 | import Layout from '../../components/layout/layout'
4 | import NoteNav from '../../components/notes/note-nav'
5 | import { graphql } from 'gatsby'
6 | const NotesIndex = ({ data, location }) => (
7 |
13 | {data.mdx.body}
14 |
15 | )
16 | export const pageQuery = graphql`
17 | query RootNoteQuery {
18 | mdx(frontmatter: { id: { eq: "652e319b-7ae5-46cb-8afb-e42f8fc600ca" } }) {
19 | id
20 | body
21 | frontmatter {
22 | title
23 | desc
24 | }
25 | }
26 | }
27 | `
28 |
29 | export default NotesIndex
30 |
--------------------------------------------------------------------------------
/src/pages/poetry/all.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import React from 'react'
3 | import Layout from '../../components/layout/layout'
4 | import PostList from '../../components/posts/post-list'
5 | import { graphql } from 'gatsby'
6 | import { jsx } from '@emotion/react'
7 |
8 | const PoemIndex = ({ data, location }) => {
9 | return (
10 |
15 |
16 |
17 | )
18 | }
19 | export const pageQuery = graphql`
20 | query poemIndex {
21 | allMdx(
22 | filter: { fileAbsolutePath: { regex: "/content/poems/" } }
23 | sort: { fields: frontmatter___title, order: ASC }
24 | ) {
25 | edges {
26 | node {
27 | frontmatter {
28 | title
29 | slug
30 | excerpt
31 | tags
32 | }
33 | }
34 | }
35 | }
36 | }
37 | `
38 | export default PoemIndex
39 |
--------------------------------------------------------------------------------
/src/pages/poetry/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | slug: '/poetry'
3 | ---
4 |
5 | import Layout from '../../components/layout/layout'
6 | import { InternalLink } from '../../components/layout/links'
7 | import {
8 | PostListStyled,
9 | PostLinkStyled,
10 | Description,
11 | } from '../../components/posts/post-list.js'
12 | export default (props, location) => (
13 |
20 | )
21 |
22 | ## Books
23 |
24 |
25 |
26 |
27 | Seasons of Thought
28 |
29 |
30 | A collection of poetry reflecting on the seasons, nature, childhood,
31 | family, and faith.
32 |
33 |
34 |
35 |
36 | ## Poems
37 |
38 | - [Evergreen](/poetry/seasons-of-thought/evergreen)
39 | - [To the Green Rock](/poetry/to-the-green-rock)
40 | - [Ink](/poetry/ink). First published by [The Quad](http://www2.gcc.edu/orgs/TheQuad/)
41 | - [First Thought](/poetry/first-thought). First published by [The Quad](http://www2.gcc.edu/orgs/TheQuad/)
42 | - [A Benediction on Marriage](/poetry/a-benediction-on-marriage)
43 | - [Evening Praise](/poetry/seasons-of-thought/evening-praise)
44 |
45 | [See all poems →](/poetry/all)
46 | [See all tags →](/poetry/tags)
47 |
48 | If you prefer to see my prose about poetry, check out the [articles I've tagged with poetry.](/articles/tag/poetry)
49 |
--------------------------------------------------------------------------------
/src/pages/poetry/seasons-of-thought.mdx:
--------------------------------------------------------------------------------
1 | import Layout from '../../components/layout/layout'
2 | import BookCard from '../../components/book-card'
3 | export default (props, location) => (
4 |
11 | )
12 |
13 | {' '}
14 |
15 | ## Containing the poems
16 |
17 | - [Advent](/poetry/seasons-of-thought/advent)
18 | - [Jazz Quartet on Christmas Eve](/poetry/seasons-of-thought/jazz-quartet-on-christmas-eve)
19 | - [The Fox](/poetry/seasons-of-thought/the-fox)
20 | - [The Top](/poetry/seasons-of-thought/the-top)
21 | - [Evening Praise](/poetry/seasons-of-thought/evening-praise)
22 | - [Evergreen](/poetry/seasons-of-thought/evergreen)
23 | - [Another](/poetry/seasons-of-thought/another)
24 | - [Sunset](/poetry/seasons-of-thought/sunset)
25 |
--------------------------------------------------------------------------------
/src/pages/privacy.mdx:
--------------------------------------------------------------------------------
1 | import Layout from '../components/layout/layout'
2 | export default (props, location) => (
3 |
10 | )
11 |
12 | # Privacy Policies
13 |
14 | Welcome to the Privacy Policies page for the projects I maintain.
15 |
16 | ## Introduction to Hebrew
17 |
18 | Privacy Policy for Introduction to Hebrew / Introduction to Hebrew Vocab
19 |
20 | Introduction to Hebrew (the "App") respects your privacy and does not collect, store, or share any user information. By using this App, you acknowledge and agree to this Privacy Policy. We reserve the right to update or change this policy without notice.
21 |
22 | ## Mileage Metrics
23 |
24 | Privacy Policy for Mileage Metrics
25 |
26 | Mileage Metrics (the "App") respects your privacy and does not collect, store, or share any user information. By using this App, you acknowledge and agree to this Privacy Policy. We reserve the right to update or change this policy without notice.
27 |
--------------------------------------------------------------------------------
/static/2021-06-03-07-35-43.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dschapman/dschapman-com/7b8c29a7f8afc3c7672b836460404aa34b35b932/static/2021-06-03-07-35-43.png
--------------------------------------------------------------------------------
/static/2021-06-05-14-49-38.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dschapman/dschapman-com/7b8c29a7f8afc3c7672b836460404aa34b35b932/static/2021-06-05-14-49-38.png
--------------------------------------------------------------------------------
/static/9a-XUSP.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dschapman/dschapman-com/7b8c29a7f8afc3c7672b836460404aa34b35b932/static/9a-XUSP.png
--------------------------------------------------------------------------------
/static/What-is-a-digital-garden.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dschapman/dschapman-com/7b8c29a7f8afc3c7672b836460404aa34b35b932/static/What-is-a-digital-garden.png
--------------------------------------------------------------------------------
/static/_redirects:
--------------------------------------------------------------------------------
1 | /notes/remove-trailing-slash-in-google-analytics /notes/d2f248e4-ddec-4e36-b29b-8879fd5a5838
2 | /notes/digital-garden /notes/5e6ba56d-11d3-4db0-a114-c8a415fd0bf6
3 | /notes/mix-monospace-and-variable-fonts-in-doom-emacs /notes/33f4867d-dbe9-4c4d-8b0a-d28ad6376128
4 | /notes/upgrade-doom-emacs-from-emacs-26-to-emacs-27 /notes/eaa58ce8-38bf-41bf-91e7-4c1481b48bbc
5 | /notes/doom-emacs /notes/fb6fe582-89ef-4d48-8b13-33e45ee3e253
6 | /notes/books /notes/c884d336-5f4d-47a5-8a8b-070240916edd
7 | /notes/networked-thinking /notes/992d44bb-e1a9-4669-80b1-d5e570dffe30
8 | /notes/digital-literacy /notes/91719f7e-dc50-4ee7-956c-32a8ca93d1a2
9 | /notes/org-roam /notes/51741cc7-789d-467a-8bd3-ffb4ff6ceb49
10 | /notes/hypertext-thinking /notes/a9339c5f-c57a-4e37-8c41-a1429ebb220b
11 | /notes/using-tippyjs-to-implement-link-previews /notes/9a430338-e918-46d5-aab8-82292ee25c32
12 | /notes/website /notes/fbb81edf-a3fa-4235-be5d-ad43f5f11da6
--------------------------------------------------------------------------------
/static/create-github-project.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dschapman/dschapman-com/7b8c29a7f8afc3c7672b836460404aa34b35b932/static/create-github-project.png
--------------------------------------------------------------------------------
/static/create-wordpress-instance.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dschapman/dschapman-com/7b8c29a7f8afc3c7672b836460404aa34b35b932/static/create-wordpress-instance.png
--------------------------------------------------------------------------------
/static/dendron-frontmatter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dschapman/dschapman-com/7b8c29a7f8afc3c7672b836460404aa34b35b932/static/dendron-frontmatter.png
--------------------------------------------------------------------------------
/static/evening-praise.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dschapman/dschapman-com/7b8c29a7f8afc3c7672b836460404aa34b35b932/static/evening-praise.mp3
--------------------------------------------------------------------------------
/static/evergreen.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dschapman/dschapman-com/7b8c29a7f8afc3c7672b836460404aa34b35b932/static/evergreen.mp3
--------------------------------------------------------------------------------
/static/how-colorsdanielchapmandev-gets-its-content.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dschapman/dschapman-com/7b8c29a7f8afc3c7672b836460404aa34b35b932/static/how-colorsdanielchapmandev-gets-its-content.png
--------------------------------------------------------------------------------
/static/little-women-pages.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dschapman/dschapman-com/7b8c29a7f8afc3c7672b836460404aa34b35b932/static/little-women-pages.png
--------------------------------------------------------------------------------
/static/my-github-learning-project.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dschapman/dschapman-com/7b8c29a7f8afc3c7672b836460404aa34b35b932/static/my-github-learning-project.png
--------------------------------------------------------------------------------
/static/org-roam-graph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dschapman/dschapman-com/7b8c29a7f8afc3c7672b836460404aa34b35b932/static/org-roam-graph.png
--------------------------------------------------------------------------------
/static/to-the-green-rock.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dschapman/dschapman-com/7b8c29a7f8afc3c7672b836460404aa34b35b932/static/to-the-green-rock.mp3
--------------------------------------------------------------------------------
/static/wordpress-instance-setup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dschapman/dschapman-com/7b8c29a7f8afc3c7672b836460404aa34b35b932/static/wordpress-instance-setup.png
--------------------------------------------------------------------------------