jekyll-theme-doctored 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +62 -0
- data/_config.yml +50 -0
- data/_includes/anchor_headings.html +174 -0
- data/_includes/foot.html +33 -0
- data/_includes/head.html +33 -0
- data/_includes/nav.html +18 -0
- data/_includes/toc-card.html +177 -0
- data/_includes/toc.html +189 -0
- data/_layouts/default.html +24 -0
- data/_layouts/page.html +15 -0
- data/_layouts/post.html +24 -0
- data/_layouts/project.html +33 -0
- data/assets/css/main.css +76 -0
- data/assets/fonts/LibertinusMonoMono.otf +0 -0
- data/assets/images/favicon.ico +0 -0
- data/assets/images/favicon.png +0 -0
- data/assets/js/scrollspy.js +62 -0
- data/assets/js/toggle-theme.js +33 -0
- data/package.json +20 -0
- data/postcss.config.js +10 -0
- data/tailwind.config.js +43 -0
- metadata +108 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 4fc26b437cc62595c064e308b8b371341f45b4f5e2b7c6fa15073a58b60eb2c0
|
|
4
|
+
data.tar.gz: 070d0a9332e3c6efdab9e8dcbf8ab287a26a7022484f817d40a42ad4e2504fd2
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 119185681985c9740c9ab3d7ca417aba336cd4e43de2e8eac649e9efd6e652061f92496cb3d4755763cd472f50071eacc95b16a44ab63078c3c5c9fe5cf74034
|
|
7
|
+
data.tar.gz: f35ead47ceb577af4d3e4c540a0541a7163af921991de6e06a04bb941a3cea6358d2f589aa6b34d333df691e140936e4d32a024600688e3bc58d8be6b11f2d80
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
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.
|
data/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# 🎩 Doctored
|
|
2
|
+
|
|
3
|
+
[](https://rubygems.org/gems/doctored)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://wakatime.com/badge/user/7482ea9d-3085-4e9b-95ad-1ca78a14d948/project/f57b75b4-8209-4d96-bb52-b673574bed86)
|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
A blog theme prioritizing uniqueness, technical complexity, and sophistication.
|
|
10
|
+
|
|
11
|
+
**Demo Deployment:** https://matmanna.github.io/doctored
|
|
12
|
+
|
|
13
|
+
## 🌟 Overview
|
|
14
|
+
|
|
15
|
+
Made using Jekyll and Tailwind following the [Tonic](https://tonic.hackclub.com) starter guide and [template](https://github.com/hackclub/tonic-starter). No other jekyll theme really felt like it matched what I would have wanted so I created my own!
|
|
16
|
+
|
|
17
|
+
**💫 Feature highlights:**
|
|
18
|
+
|
|
19
|
+
- 20+ color schemes & dark/light/system modes
|
|
20
|
+
- Projects, blog, now, and contact pages
|
|
21
|
+
- GFM support (tables, admonitions, etc.)
|
|
22
|
+
- Git metadata access
|
|
23
|
+
- Floating Table of Contents
|
|
24
|
+
- Side & foot notes
|
|
25
|
+
- RSS feed & sitemap
|
|
26
|
+
- Responsive nav, footer sections
|
|
27
|
+
|
|
28
|
+
## 📸 Demo Images:
|
|
29
|
+
|
|
30
|
+
| <img width="1919" height="1020" alt="image" src="https://github.com/user-attachments/assets/a130fd59-c673-4f4a-a676-3fd0bfb2473b" /> | <img width="1914" height="1018" alt="image" src="https://github.com/user-attachments/assets/2eeb6c33-fe38-46a4-8d54-c1fad985ed3b" /> |
|
|
31
|
+
| ----- | ---- |
|
|
32
|
+
| <img width="1919" height="1019" alt="image" src="https://github.com/user-attachments/assets/0919fd1d-2ed2-4ded-829a-6b504f939515" /> | <img width="1917" height="1014" alt="image" src="https://github.com/user-attachments/assets/dadb5548-a2c0-4f22-a2e1-6b2f67bd29f9" /> |
|
|
33
|
+
|
|
34
|
+
## ⌨️ Usage
|
|
35
|
+
|
|
36
|
+
> [!IMPORTANT]
|
|
37
|
+
> Developing a site with doctored requires having Ruby, RubyGems, GCC, Make Bundle, Python, Node.js, and npm installed on your system.
|
|
38
|
+
|
|
39
|
+
### ▶️ Running
|
|
40
|
+
|
|
41
|
+
Run `npm install` and `npm run dev` to develop. Run `npm run build` to build. Postinstall and other scripts have been configured to use `bundle install`, `bundle exec jekyll serve --watch`, and `bundle exec jekyll build` respectively.
|
|
42
|
+
|
|
43
|
+
### 🖼️ Formatting posts
|
|
44
|
+
|
|
45
|
+
To post a new post, run `npm run post markdown_file_path_in__drafts`, which formats it with side/foot notes then moves it to `_posts/` (or root if page!)
|
|
46
|
+
|
|
47
|
+
Based on [@JacobU](https://github.com/jacobu)'s `postMarkdown` python script, doctored supports writing your posts/pages in a simple markdown file within `_drafts` before running the `post` script to format it in the following ways:
|
|
48
|
+
|
|
49
|
+
- **Foot/sidenotes:**
|
|
50
|
+
|
|
51
|
+
To add a foot/side note, include the following in your draft markdown:
|
|
52
|
+
```html
|
|
53
|
+
<small>Richly-formatted (html) content of note</small>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## 🙌 Contributing
|
|
57
|
+
|
|
58
|
+
Please create Issues and PRs for bugs/feature requests and updated code respectively.
|
|
59
|
+
|
|
60
|
+
### 📜 License
|
|
61
|
+
|
|
62
|
+
Doctored is licensed under the MIT, which means you can do (almost) anything with it!!
|
data/_config.yml
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
title: Doctored
|
|
2
|
+
description: "A blog theme prioritizing uniqueness, complexity, and sophistication."
|
|
3
|
+
github: "https://github.com/matmanna/doctored"
|
|
4
|
+
url: "https://matmanna.github.io/doctored"
|
|
5
|
+
encoding: utf-8
|
|
6
|
+
|
|
7
|
+
plugins:
|
|
8
|
+
- jekyll-postcss
|
|
9
|
+
- jekyll-git_metadata
|
|
10
|
+
- kramdown-parser-gfm
|
|
11
|
+
- jekyll-gfm-admonitions
|
|
12
|
+
- jekyll-sitemap
|
|
13
|
+
- jekyll-feed
|
|
14
|
+
|
|
15
|
+
feed:
|
|
16
|
+
icon: /assets/images/favicon.png
|
|
17
|
+
|
|
18
|
+
redcarpet:
|
|
19
|
+
extensions: ["with_toc_data"]
|
|
20
|
+
|
|
21
|
+
collections:
|
|
22
|
+
projects:
|
|
23
|
+
output: true
|
|
24
|
+
pages:
|
|
25
|
+
permalink: /:title/
|
|
26
|
+
output: true
|
|
27
|
+
|
|
28
|
+
exclude:
|
|
29
|
+
- README.md
|
|
30
|
+
- LICENSE
|
|
31
|
+
- "*.gem"
|
|
32
|
+
- "*.gemspec"
|
|
33
|
+
- vendor
|
|
34
|
+
- Gemfile.lock
|
|
35
|
+
- .gitignore
|
|
36
|
+
- bin
|
|
37
|
+
- node_modules
|
|
38
|
+
- package-lock.json
|
|
39
|
+
- postcss.config.js
|
|
40
|
+
|
|
41
|
+
sass:
|
|
42
|
+
sourcemap: never
|
|
43
|
+
|
|
44
|
+
postcss:
|
|
45
|
+
cache: false
|
|
46
|
+
|
|
47
|
+
keep_files:
|
|
48
|
+
- assets/css/tailwind.css
|
|
49
|
+
- assetss/css/tailwind.css.map
|
|
50
|
+
- assets/js
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
{% capture headingsWorkspace %}
|
|
2
|
+
{% comment %}
|
|
3
|
+
Copyright (c) 2018 Vladimir "allejo" Jimenez
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person
|
|
6
|
+
obtaining a copy of this software and associated documentation
|
|
7
|
+
files (the "Software"), to deal in the Software without
|
|
8
|
+
restriction, including without limitation the rights to use,
|
|
9
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the
|
|
11
|
+
Software is furnished to do so, subject to the following
|
|
12
|
+
conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be
|
|
15
|
+
included in all copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
19
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
21
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
22
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
23
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
24
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
25
|
+
{% endcomment %}
|
|
26
|
+
{% comment %}
|
|
27
|
+
Version 1.0.13
|
|
28
|
+
https://github.com/allejo/jekyll-anchor-headings
|
|
29
|
+
|
|
30
|
+
"Be the pull request you wish to see in the world." ~Ben Balter
|
|
31
|
+
|
|
32
|
+
Usage:
|
|
33
|
+
{% include anchor_headings.html html=content anchorBody="#" %}
|
|
34
|
+
|
|
35
|
+
Parameters:
|
|
36
|
+
* html (string) - the HTML of compiled markdown generated by kramdown in Jekyll
|
|
37
|
+
|
|
38
|
+
Optional Parameters:
|
|
39
|
+
* beforeHeading (bool) : false - Set to true if the anchor should be placed _before_ the heading's content
|
|
40
|
+
* headerAttrs (string) : '' - Any custom HTML attributes that will be added to the heading tag; you may NOT use `id`;
|
|
41
|
+
the `%heading%` and `%html_id%` placeholders are available
|
|
42
|
+
* anchorAttrs (string) : '' - Any custom HTML attributes that will be added to the `<a>` tag; you may NOT use `href`, `class` or `title`;
|
|
43
|
+
the `%heading%` and `%html_id%` placeholders are available
|
|
44
|
+
* anchorBody (string) : '' - The content that will be placed inside the anchor; the `%heading%` placeholder is available
|
|
45
|
+
* anchorClass (string) : '' - The class(es) that will be used for each anchor. Separate multiple classes with a space
|
|
46
|
+
* anchorTitle (string) : '' - The `title` attribute that will be used for anchors
|
|
47
|
+
* h_min (int) : 1 - The minimum header level to build an anchor for; any header lower than this value will be ignored
|
|
48
|
+
* h_max (int) : 6 - The maximum header level to build an anchor for; any header greater than this value will be ignored
|
|
49
|
+
* bodyPrefix (string) : '' - Anything that should be inserted inside of the heading tag _before_ its anchor and content
|
|
50
|
+
* bodySuffix (string) : '' - Anything that should be inserted inside of the heading tag _after_ its anchor and content
|
|
51
|
+
* generateId (true) : false - Set to true if a header without id should generate an id to use.
|
|
52
|
+
|
|
53
|
+
Output:
|
|
54
|
+
The original HTML with the addition of anchors inside of all of the h1-h6 headings.
|
|
55
|
+
{% endcomment %}
|
|
56
|
+
|
|
57
|
+
{% assign minHeader = include.h_min | default: 1 %}
|
|
58
|
+
{% assign maxHeader = include.h_max | default: 6 %}
|
|
59
|
+
{% assign beforeHeading = include.beforeHeading %}
|
|
60
|
+
{% assign headerAttrs = include.headerAttrs %}
|
|
61
|
+
{% assign nodes = include.html | split: '<h' %}
|
|
62
|
+
|
|
63
|
+
{% capture edited_headings %}{% endcapture %}
|
|
64
|
+
|
|
65
|
+
{% for _node in nodes %}
|
|
66
|
+
{% capture node %}{{ _node | strip }}{% endcapture %}
|
|
67
|
+
|
|
68
|
+
{% if node == "" %}
|
|
69
|
+
{% continue %}
|
|
70
|
+
{% endif %}
|
|
71
|
+
|
|
72
|
+
{% assign nextChar = node | replace: '"', '' | strip | slice: 0, 1 %}
|
|
73
|
+
{% assign headerLevel = nextChar | times: 1 %}
|
|
74
|
+
|
|
75
|
+
<!-- If the level is cast to 0, it means it's not a h1-h6 tag, so let's see if we need to fix it -->
|
|
76
|
+
{% if headerLevel == 0 %}
|
|
77
|
+
<!-- Split up the node based on closing angle brackets and get the first one. -->
|
|
78
|
+
{% assign firstChunk = node | split: '>' | first %}
|
|
79
|
+
|
|
80
|
+
<!-- If the first chunk does NOT contain a '<', that means we've broken another HTML tag that starts with 'h' -->
|
|
81
|
+
{% unless firstChunk contains '<' %}
|
|
82
|
+
{% capture node %}<h{{ node }}{% endcapture %}
|
|
83
|
+
{% endunless %}
|
|
84
|
+
|
|
85
|
+
{% capture edited_headings %}{{ edited_headings }}{{ node }}{% endcapture %}
|
|
86
|
+
{% continue %}
|
|
87
|
+
{% endif %}
|
|
88
|
+
|
|
89
|
+
{% capture _closingTag %}</h{{ headerLevel }}>{% endcapture %}
|
|
90
|
+
{% assign _workspace = node | split: _closingTag %}
|
|
91
|
+
{% capture _hAttrToStrip %}{{ _workspace[0] | split: '>' | first }}>{% endcapture %}
|
|
92
|
+
{% assign header = _workspace[0] | replace: _hAttrToStrip, '' %}
|
|
93
|
+
{% assign escaped_header = header | strip_html | strip %}
|
|
94
|
+
|
|
95
|
+
{% assign _classWorkspace = _workspace[0] | split: 'class="' %}
|
|
96
|
+
{% assign _classWorkspace = _classWorkspace[1] | split: '"' %}
|
|
97
|
+
{% assign _html_class = _classWorkspace[0] %}
|
|
98
|
+
|
|
99
|
+
{% if _html_class contains "no_anchor" %}
|
|
100
|
+
{% assign skip_anchor = true %}
|
|
101
|
+
{% else %}
|
|
102
|
+
{% assign skip_anchor = false %}
|
|
103
|
+
{% endif %}
|
|
104
|
+
|
|
105
|
+
{% assign _idWorkspace = _workspace[0] | split: 'id="' %}
|
|
106
|
+
{% if _idWorkspace[1] %}
|
|
107
|
+
{% assign _idWorkspace = _idWorkspace[1] | split: '"' %}
|
|
108
|
+
{% assign html_id = _idWorkspace[0] %}
|
|
109
|
+
{% assign h_attrs = headerAttrs %}
|
|
110
|
+
{% elsif include.generateId %}
|
|
111
|
+
<!-- If the header did not have an id we create one. -->
|
|
112
|
+
{% assign html_id = escaped_header | slugify %}
|
|
113
|
+
{% if html_id == "" %}
|
|
114
|
+
{% assign html_id = false %}
|
|
115
|
+
{% endif %}
|
|
116
|
+
<!-- Append the generated id to other potential header attributes. -->
|
|
117
|
+
{% capture h_attrs %}{{ headerAttrs }} id="%html_id%"{% endcapture %}
|
|
118
|
+
{% endif %}
|
|
119
|
+
|
|
120
|
+
<!-- Build the anchor to inject for our heading -->
|
|
121
|
+
{% capture anchor %}{% endcapture %}
|
|
122
|
+
|
|
123
|
+
{% if skip_anchor == false and html_id and headerLevel >= minHeader and headerLevel <= maxHeader %}
|
|
124
|
+
{% if h_attrs %}
|
|
125
|
+
{% capture _hAttrToStrip %}{{ _hAttrToStrip | split: '>' | first }} {{ h_attrs | strip | replace: '%heading%', escaped_header | replace: '%html_id%', html_id }}>{% endcapture %}
|
|
126
|
+
{% endif %}
|
|
127
|
+
|
|
128
|
+
{% capture anchor %}href="#{{ html_id }}"{% endcapture %}
|
|
129
|
+
|
|
130
|
+
{% if include.anchorClass %}
|
|
131
|
+
{% capture anchor %}{{ anchor }} class="{{ include.anchorClass }}"{% endcapture %}
|
|
132
|
+
{% endif %}
|
|
133
|
+
|
|
134
|
+
{% if include.anchorTitle %}
|
|
135
|
+
{% capture anchor %}{{ anchor }} title="{{ include.anchorTitle | replace: '%heading%', escaped_header }}"{% endcapture %}
|
|
136
|
+
{% endif %}
|
|
137
|
+
|
|
138
|
+
{% if include.anchorAttrs %}
|
|
139
|
+
{% capture anchor %}{{ anchor }} {{ include.anchorAttrs | replace: '%heading%', escaped_header | replace: '%html_id%', html_id }}{% endcapture %}
|
|
140
|
+
{% endif %}
|
|
141
|
+
|
|
142
|
+
{% capture anchor %}<a {{ anchor }}>{{ include.anchorBody | replace: '%heading%', escaped_header | default: '' }}</a>{% endcapture %}
|
|
143
|
+
|
|
144
|
+
<!-- In order to prevent adding extra space after a heading, we'll let the 'anchor' value contain it -->
|
|
145
|
+
{% if beforeHeading %}
|
|
146
|
+
{% capture anchor %}{{ anchor }} {% endcapture %}
|
|
147
|
+
{% else %}
|
|
148
|
+
{% capture anchor %} {{ anchor }}{% endcapture %}
|
|
149
|
+
{% endif %}
|
|
150
|
+
{% endif %}
|
|
151
|
+
|
|
152
|
+
{% capture new_heading %}
|
|
153
|
+
<h{{ _hAttrToStrip }}
|
|
154
|
+
{{ include.bodyPrefix }}
|
|
155
|
+
{% if beforeHeading %}
|
|
156
|
+
{{ anchor }}{{ header }}
|
|
157
|
+
{% else %}
|
|
158
|
+
{{ header }}{{ anchor }}
|
|
159
|
+
{% endif %}
|
|
160
|
+
{{ include.bodySuffix }}
|
|
161
|
+
</h{{ headerLevel }}>
|
|
162
|
+
{% endcapture %}
|
|
163
|
+
|
|
164
|
+
<!--
|
|
165
|
+
If we have content after the `</hX>` tag, then we'll want to append that here so we don't lost any content.
|
|
166
|
+
-->
|
|
167
|
+
{% assign chunkCount = _workspace | size %}
|
|
168
|
+
{% if chunkCount > 1 %}
|
|
169
|
+
{% capture new_heading %}{{ new_heading }}{{ _workspace | last }}{% endcapture %}
|
|
170
|
+
{% endif %}
|
|
171
|
+
|
|
172
|
+
{% capture edited_headings %}{{ edited_headings }}{{ new_heading }}{% endcapture %}
|
|
173
|
+
{% endfor %}
|
|
174
|
+
{% endcapture %}{% assign headingsWorkspace = '' %}{{ edited_headings | strip }}
|
data/_includes/foot.html
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<div class="flex flex-row justify-between w-full px-5 md:px-10 gap-8">
|
|
2
|
+
<div class="flex flex-col space-y-4 sm:flex-grow max-w-[50%] font-sans">
|
|
3
|
+
<h3 class="block">{{ site.data.foot.emoji }} {{ site.data.foot.title }} | <span class="dark:text-primary-600 text-primary-700">{{ site.description }}</span></h3>
|
|
4
|
+
<div class="flex flex-row flex-wrap gap-4 dark:text-primary-600">
|
|
5
|
+
<label for="theme">Color mode:</label>
|
|
6
|
+
|
|
7
|
+
<select name="theme" id="theme-dropdown" class="flex flex-shrink w-fit bg-transparent">
|
|
8
|
+
<option value="dark">Dark</option>
|
|
9
|
+
<option value="light">Light</option>
|
|
10
|
+
<option value="system">System</option>
|
|
11
|
+
</select>
|
|
12
|
+
</div>
|
|
13
|
+
<p>Site built on {{ site.git.last_commit.commit_date }} from
|
|
14
|
+
<a
|
|
15
|
+
href="{{ site.github }}/commit/{{ site.git.last_commit.long_sha }}"><code>{{ site.git.last_commit.short_sha }}</code></a>
|
|
16
|
+
</p>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="flex flex-row flex-wrap justify-between flex-grow gap-4">
|
|
19
|
+
<ul class="flex flex-col space-y-4">
|
|
20
|
+
<li class="font-bold">quick links</li>
|
|
21
|
+
{% for entry in site.data.nav.pages %}
|
|
22
|
+
<li><a href="{{ entry.url | relative_url }}">/{{ entry.title }}</a></li>
|
|
23
|
+
{% endfor %}
|
|
24
|
+
</ul>
|
|
25
|
+
<ul class="flex flex-col space-y-4">
|
|
26
|
+
<li class="font-bold" id="contact">socials</li>
|
|
27
|
+
|
|
28
|
+
{% for social in site.data.foot.socials %}
|
|
29
|
+
<li><a href="{{ social.url }}">:{{ social.title }}</a></li>
|
|
30
|
+
{% endfor %}
|
|
31
|
+
</ul>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
data/_includes/head.html
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<head>
|
|
2
|
+
<title>{{ page.title }} | {{ site.title }}</title>
|
|
3
|
+
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<script>
|
|
7
|
+
function applyTheme(newTheme, triggeredByDropdown = false) {
|
|
8
|
+
const isDarkMode = newTheme === 'dark' || (localStorage.theme == 'dark' && !(newTheme === 'light')) || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
|
9
|
+
document.documentElement.classList.toggle(
|
|
10
|
+
'dark',
|
|
11
|
+
isDarkMode
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
const toggle = document.getElementById('theme-toggle');
|
|
15
|
+
const dropdown = document.getElementById('theme-dropdown');
|
|
16
|
+
|
|
17
|
+
if (toggle) {
|
|
18
|
+
toggle.innerHTML =
|
|
19
|
+
localStorage.theme == "dark" ? '<i class="fas fa-moon"></i>' : !localStorage.theme ? '<i class="fas fa-laptop"></i>' : '<i class="fas fa-sun"></i>';
|
|
20
|
+
}
|
|
21
|
+
if (dropdown) {
|
|
22
|
+
if (!triggeredByDropdown) {
|
|
23
|
+
dropdown.dataset.changed = new Date().toISOString();
|
|
24
|
+
}
|
|
25
|
+
dropdown.value = localStorage.theme || "system";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
applyTheme();
|
|
29
|
+
</script>
|
|
30
|
+
<link rel="stylesheet" href="{{ '/assets/css/main.css' | relative_url }}">
|
|
31
|
+
<link rel="icon" href="{{ '/assets/images/favicon.ico' | relative_url }}" type="image/x-icon">
|
|
32
|
+
<script src="https://kit.fontawesome.com/a7b73c75c9.js" crossorigin="anonymous"></script>
|
|
33
|
+
</head>
|
data/_includes/nav.html
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<div class="flex flex-row flex-wrap justify-between w-full gap-4">
|
|
2
|
+
<div class="flex flex-row gap-1">
|
|
3
|
+
{{site.data.nav.emoji}}
|
|
4
|
+
<a href="{{ '/' | relative_url }}"><h3 class="font-sans">{{ site.data.nav.title }}</h3></a>
|
|
5
|
+
</div>
|
|
6
|
+
<ul class="flex flex-row flex-wrap gap-3 sm:lips:gap-4 text-sm sm:text-base">
|
|
7
|
+
{% for entry in site.data.nav.pages %}
|
|
8
|
+
<li>
|
|
9
|
+
<a href="{{ entry.url | relative_url }}"
|
|
10
|
+
{% if page.url == entry.url or (page.url == '/' and entry.url == '/') %}
|
|
11
|
+
class="font-bold"
|
|
12
|
+
{% endif %}
|
|
13
|
+
>/{{ entry.title }}</a>
|
|
14
|
+
</li>
|
|
15
|
+
{% endfor %}
|
|
16
|
+
<li id="theme-toggle" class="dark:text-primary-400 text-primary-800 "></li>
|
|
17
|
+
</ul>
|
|
18
|
+
</div>
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
<div id="toc-container" class="relative pt-6">
|
|
2
|
+
<div id="toc-placeholder" class="blur-sm my-4 rounded-md w-full bg-primary-200 dark:bg-primary-800"></div>
|
|
3
|
+
<div id="table-of-contents" class="bg-primary-200 dark:bg-primary-800 rounded-md p-2 w-full transition-all duration-100 ease-in-out">
|
|
4
|
+
<h4 class="font-bold my-0 mt-2 ml-3"> {{ page.title }} </h4>
|
|
5
|
+
{% include toc.html html=content anchor_class="toc-link" %}
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<div id="toc-navbar" class="hidden bg-primary-300 dark:bg-primary-950 rounded-t-md p-2 w-full flex justify-between items-center opacity-0 transition-all duration-100 ease-in-out transform -translate-y-4">
|
|
9
|
+
<div class="flex items-center gap-2">
|
|
10
|
+
<span class="text-lg">{{ site.data.nav.emoji }}</span>
|
|
11
|
+
<span class="font-bold">{{ site.data.nav.title }}</span>
|
|
12
|
+
</div>
|
|
13
|
+
<div class="relative group">
|
|
14
|
+
<button class="flex items-center gap-1 px-2 py-1 rounded hover:bg-primary-400 dark:hover:bg-primary-700 transition-colors">
|
|
15
|
+
Navigate <i class="fa-solid fa-chevron-down text-xs"></i>
|
|
16
|
+
</button>
|
|
17
|
+
<div class="absolute right-0 mt-0 w-40 bg-white dark:bg-primary-900 rounded-md shadow-lg overflow-hidden z-10 hidden group-hover:block">
|
|
18
|
+
<div class="py-1">
|
|
19
|
+
{% for page in site.data.nav.pages %}
|
|
20
|
+
<a href="{{ page.url | relative_url }}" class="block px-4 py-2 text-sm hover:bg-primary-200 dark:hover:bg-primary-700">
|
|
21
|
+
/{{ page.title }}
|
|
22
|
+
</a>
|
|
23
|
+
{% endfor %}
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<script>
|
|
31
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
32
|
+
const toc = document.getElementById('table-of-contents');
|
|
33
|
+
const tocNavbar = document.getElementById('toc-navbar');
|
|
34
|
+
const placeholder = document.getElementById('toc-placeholder');
|
|
35
|
+
const tocContainer = document.getElementById('toc-container');
|
|
36
|
+
let originalRect = null;
|
|
37
|
+
let isFixed = false;
|
|
38
|
+
let targetPosition = { x: 0, y: 0, scale: 1 };
|
|
39
|
+
|
|
40
|
+
function updateOriginalPosition() {
|
|
41
|
+
toc.style.position = 'static';
|
|
42
|
+
toc.querySelector('h4').innerHTML = 'Table of Contents:';
|
|
43
|
+
toc.style.transform = 'none';
|
|
44
|
+
placeholder.style.display = 'none';
|
|
45
|
+
tocNavbar.style.display = 'none';
|
|
46
|
+
|
|
47
|
+
void toc.offsetWidth;
|
|
48
|
+
|
|
49
|
+
originalRect = toc.getBoundingClientRect();
|
|
50
|
+
placeholder.style.height = originalRect.height + 'px';
|
|
51
|
+
|
|
52
|
+
const fixedTop = 80;
|
|
53
|
+
|
|
54
|
+
targetPosition = {
|
|
55
|
+
x: 0,
|
|
56
|
+
y: fixedTop,
|
|
57
|
+
width: originalRect.width
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function toggleFixedPosition() {
|
|
62
|
+
if (!originalRect) {
|
|
63
|
+
updateOriginalPosition();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (window.innerWidth >= 1280) {
|
|
67
|
+
const shouldBeFixed = window.scrollY > (tocContainer.offsetTop - 5);
|
|
68
|
+
|
|
69
|
+
if (shouldBeFixed !== isFixed) {
|
|
70
|
+
isFixed = shouldBeFixed;
|
|
71
|
+
|
|
72
|
+
if (isFixed) {
|
|
73
|
+
const viewportWidth = window.innerWidth;
|
|
74
|
+
let fixedLeft, maxWidth;
|
|
75
|
+
|
|
76
|
+
if (viewportWidth > 1600) {
|
|
77
|
+
fixedLeft = viewportWidth * 0.03;
|
|
78
|
+
maxWidth = Math.min(450, viewportWidth * 0.25);
|
|
79
|
+
} else if (viewportWidth > 1280) {
|
|
80
|
+
fixedLeft = viewportWidth * 0.025;
|
|
81
|
+
maxWidth = Math.min(400, viewportWidth * 0.22);
|
|
82
|
+
} else {
|
|
83
|
+
fixedLeft = viewportWidth * 0.03;
|
|
84
|
+
maxWidth = Math.min(350, viewportWidth * 0.2);
|
|
85
|
+
}
|
|
86
|
+
toc.querySelector('h4').innerHTML = '{{ page.title }}';
|
|
87
|
+
|
|
88
|
+
targetPosition.x = fixedLeft;
|
|
89
|
+
targetPosition.width = maxWidth;
|
|
90
|
+
|
|
91
|
+
toc.style.position = 'fixed';
|
|
92
|
+
toc.style.top = targetPosition.y + 'px';
|
|
93
|
+
toc.style.left = targetPosition.x + 'px';
|
|
94
|
+
toc.style.width = targetPosition.width + 'px';
|
|
95
|
+
toc.style.borderTopLeftRadius = '0';
|
|
96
|
+
toc.style.borderTopRightRadius = '0';
|
|
97
|
+
placeholder.style.display = 'block';
|
|
98
|
+
|
|
99
|
+
tocNavbar.style.display = 'flex';
|
|
100
|
+
tocNavbar.style.position = 'fixed';
|
|
101
|
+
tocNavbar.style.top = (targetPosition.y - tocNavbar.offsetHeight) + 'px';
|
|
102
|
+
tocNavbar.style.left = targetPosition.x + 'px';
|
|
103
|
+
tocNavbar.style.width = targetPosition.width + 'px';
|
|
104
|
+
tocNavbar.style.opacity = '0';
|
|
105
|
+
tocNavbar.style.transform = 'translateY(-16px)';
|
|
106
|
+
|
|
107
|
+
void tocNavbar.offsetWidth;
|
|
108
|
+
|
|
109
|
+
requestAnimationFrame(() => {
|
|
110
|
+
tocNavbar.style.opacity = '1';
|
|
111
|
+
tocNavbar.style.transform = 'translateY(0)';
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
} else {
|
|
115
|
+
tocNavbar.style.opacity = '0';
|
|
116
|
+
tocNavbar.style.transform = 'translateY(-16px)';
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
setTimeout(() => {
|
|
120
|
+
toc.style.position = 'absolute';
|
|
121
|
+
toc.style.top = placeholder.offsetTop + 'px';
|
|
122
|
+
toc.style.left = '';
|
|
123
|
+
toc.style.width = '';
|
|
124
|
+
toc.style.maxWidth = '';
|
|
125
|
+
toc.querySelector('h4').innerHTML = 'Table of Contents:';
|
|
126
|
+
toc.style.borderTopLeftRadius = '';
|
|
127
|
+
toc.style.borderTopRightRadius = '';
|
|
128
|
+
|
|
129
|
+
tocNavbar.style.display = 'none';
|
|
130
|
+
|
|
131
|
+
void toc.offsetWidth;
|
|
132
|
+
|
|
133
|
+
setTimeout(() => {
|
|
134
|
+
placeholder.style.display = 'none';
|
|
135
|
+
toc.style.position = 'static';
|
|
136
|
+
}, 50);
|
|
137
|
+
}, 100);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} else if (isFixed) {
|
|
141
|
+
isFixed = false;
|
|
142
|
+
toc.style.position = 'static';
|
|
143
|
+
toc.style.width = 'auto';
|
|
144
|
+
toc.style.transform = 'none';
|
|
145
|
+
placeholder.style.display = 'none';
|
|
146
|
+
tocNavbar.style.display = 'none';
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let ticking = false;
|
|
151
|
+
window.addEventListener('scroll', function() {
|
|
152
|
+
if (!ticking) {
|
|
153
|
+
window.requestAnimationFrame(function() {
|
|
154
|
+
toggleFixedPosition();
|
|
155
|
+
ticking = false;
|
|
156
|
+
});
|
|
157
|
+
ticking = true;
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
let scrollTimeout;
|
|
162
|
+
window.addEventListener('scroll', () => {
|
|
163
|
+
clearTimeout(scrollTimeout);
|
|
164
|
+
scrollTimeout = setTimeout(() => {
|
|
165
|
+
toggleFixedPosition();
|
|
166
|
+
}, 50);
|
|
167
|
+
}, { passive: true });
|
|
168
|
+
|
|
169
|
+
window.addEventListener('resize', function() {
|
|
170
|
+
updateOriginalPosition();
|
|
171
|
+
toggleFixedPosition();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
updateOriginalPosition();
|
|
175
|
+
toggleFixedPosition();
|
|
176
|
+
});
|
|
177
|
+
</script>
|
data/_includes/toc.html
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
{% capture tocWorkspace %}
|
|
2
|
+
{% comment %}
|
|
3
|
+
Copyright (c) 2017 Vladimir "allejo" Jimenez
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person
|
|
6
|
+
obtaining a copy of this software and associated documentation
|
|
7
|
+
files (the "Software"), to deal in the Software without
|
|
8
|
+
restriction, including without limitation the rights to use,
|
|
9
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the
|
|
11
|
+
Software is furnished to do so, subject to the following
|
|
12
|
+
conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be
|
|
15
|
+
included in all copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
19
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
21
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
22
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
23
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
24
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
25
|
+
{% endcomment %}
|
|
26
|
+
{% comment %}
|
|
27
|
+
Version 1.2.1
|
|
28
|
+
https://github.com/allejo/jekyll-toc
|
|
29
|
+
|
|
30
|
+
"...like all things liquid - where there's a will, and ~36 hours to spare, there's usually a/some way" ~jaybe
|
|
31
|
+
|
|
32
|
+
Usage:
|
|
33
|
+
{% include toc.html html=content sanitize=true class="inline_toc" id="my_toc" h_min=2 h_max=3 %}
|
|
34
|
+
|
|
35
|
+
Parameters:
|
|
36
|
+
* html (string) - the HTML of compiled markdown generated by kramdown in Jekyll
|
|
37
|
+
|
|
38
|
+
Optional Parameters:
|
|
39
|
+
* sanitize (bool) : false - when set to true, the headers will be stripped of any HTML in the TOC
|
|
40
|
+
* class (string) : '' - a CSS class assigned to the TOC
|
|
41
|
+
* id (string) : '' - an ID to assigned to the TOC
|
|
42
|
+
* h_min (int) : 1 - the minimum TOC header level to use; any header lower than this value will be ignored
|
|
43
|
+
* h_max (int) : 6 - the maximum TOC header level to use; any header greater than this value will be ignored
|
|
44
|
+
* ordered (bool) : false - when set to true, an ordered list will be outputted instead of an unordered list
|
|
45
|
+
* item_class (string) : '' - add custom class(es) for each list item; has support for '%level%' placeholder, which is the current heading level
|
|
46
|
+
* submenu_class (string) : '' - add custom class(es) for each child group of headings; has support for '%level%' placeholder which is the current "submenu" heading level
|
|
47
|
+
* base_url (string) : '' - add a base url to the TOC links for when your TOC is on another page than the actual content
|
|
48
|
+
* anchor_class (string) : '' - add custom class(es) for each anchor element
|
|
49
|
+
* skip_no_ids (bool) : false - skip headers that do not have an `id` attribute
|
|
50
|
+
* flat_toc (bool) : false - when set to true, the TOC will be a single level list
|
|
51
|
+
|
|
52
|
+
Output:
|
|
53
|
+
An ordered or unordered list representing the table of contents of a markdown block. This snippet will only
|
|
54
|
+
generate the table of contents and will NOT output the markdown given to it
|
|
55
|
+
{% endcomment %}
|
|
56
|
+
|
|
57
|
+
{% capture newline %}
|
|
58
|
+
{% endcapture %}
|
|
59
|
+
{% assign newline = newline | rstrip %} <!-- Remove the extra spacing but preserve the newline -->
|
|
60
|
+
|
|
61
|
+
{% capture deprecation_warnings %}{% endcapture %}
|
|
62
|
+
|
|
63
|
+
{% if include.baseurl %}
|
|
64
|
+
{% capture deprecation_warnings %}{{ deprecation_warnings }}<!-- jekyll-toc :: "baseurl" has been deprecated, use "base_url" instead -->{{ newline }}{% endcapture %}
|
|
65
|
+
{% endif %}
|
|
66
|
+
|
|
67
|
+
{% if include.skipNoIDs %}
|
|
68
|
+
{% capture deprecation_warnings %}{{ deprecation_warnings }}<!-- jekyll-toc :: "skipNoIDs" has been deprecated, use "skip_no_ids" instead -->{{ newline }}{% endcapture %}
|
|
69
|
+
{% endif %}
|
|
70
|
+
|
|
71
|
+
{% capture jekyll_toc %}{% endcapture %}
|
|
72
|
+
{% assign orderedList = include.ordered | default: false %}
|
|
73
|
+
{% assign flatToc = include.flat_toc | default: false %}
|
|
74
|
+
{% assign baseURL = include.base_url | default: include.baseurl | default: '' %}
|
|
75
|
+
{% assign skipNoIDs = include.skip_no_ids | default: include.skipNoIDs | default: false %}
|
|
76
|
+
{% assign minHeader = include.h_min | default: 1 %}
|
|
77
|
+
{% assign maxHeader = include.h_max | default: 6 %}
|
|
78
|
+
{% assign nodes = include.html | strip | split: '<h' %}
|
|
79
|
+
|
|
80
|
+
{% assign firstHeader = true %}
|
|
81
|
+
{% assign currLevel = 0 %}
|
|
82
|
+
{% assign lastLevel = 0 %}
|
|
83
|
+
|
|
84
|
+
{% capture listModifier %}{% if orderedList %}ol{% else %}ul{% endif %}{% endcapture %}
|
|
85
|
+
|
|
86
|
+
{% for node in nodes %}
|
|
87
|
+
{% if node == "" %}
|
|
88
|
+
{% continue %}
|
|
89
|
+
{% endif %}
|
|
90
|
+
|
|
91
|
+
{% assign currLevel = node | replace: '"', '' | slice: 0, 1 | times: 1 %}
|
|
92
|
+
|
|
93
|
+
{% if currLevel < minHeader or currLevel > maxHeader %}
|
|
94
|
+
{% continue %}
|
|
95
|
+
{% endif %}
|
|
96
|
+
|
|
97
|
+
{% assign _workspace = node | split: '</h' %}
|
|
98
|
+
|
|
99
|
+
{% assign _idWorkspace = _workspace[0] | split: 'id="' %}
|
|
100
|
+
{% assign _idWorkspace = _idWorkspace[1] | split: '"' %}
|
|
101
|
+
{% assign htmlID = _idWorkspace[0] %}
|
|
102
|
+
|
|
103
|
+
{% assign _classWorkspace = _workspace[0] | split: 'class="' %}
|
|
104
|
+
{% assign _classWorkspace = _classWorkspace[1] | split: '"' %}
|
|
105
|
+
{% assign htmlClass = _classWorkspace[0] %}
|
|
106
|
+
|
|
107
|
+
{% if htmlClass contains "no_toc" %}
|
|
108
|
+
{% continue %}
|
|
109
|
+
{% endif %}
|
|
110
|
+
|
|
111
|
+
{% if firstHeader %}
|
|
112
|
+
{% assign minHeader = currLevel %}
|
|
113
|
+
{% endif %}
|
|
114
|
+
|
|
115
|
+
{% capture _hAttrToStrip %}{{ _workspace[0] | split: '>' | first }}>{% endcapture %}
|
|
116
|
+
{% assign header = _workspace[0] | replace: _hAttrToStrip, '' %}
|
|
117
|
+
|
|
118
|
+
{% if include.item_class and include.item_class != blank %}
|
|
119
|
+
{% capture listItemClass %} class="{{ include.item_class | replace: '%level%', currLevel | split: '.' | join: ' ' }}"{% endcapture %}
|
|
120
|
+
{% endif %}
|
|
121
|
+
|
|
122
|
+
{% if include.submenu_class and include.submenu_class != blank %}
|
|
123
|
+
{% assign subMenuLevel = currLevel | minus: 1 %}
|
|
124
|
+
{% capture subMenuClass %} class="{{ include.submenu_class | replace: '%level%', subMenuLevel | split: '.' | join: ' ' }}"{% endcapture %}
|
|
125
|
+
{% endif %}
|
|
126
|
+
|
|
127
|
+
{% capture anchorBody %}{% if include.sanitize %}{{ header | strip_html }}{% else %}{{ header }}{% endif %}{% endcapture %}
|
|
128
|
+
|
|
129
|
+
{% if htmlID %}
|
|
130
|
+
{% capture anchorAttributes %} href="{% if baseURL %}{{ baseURL }}{% endif %}#{{ htmlID }}"{% endcapture %}
|
|
131
|
+
|
|
132
|
+
{% if include.anchor_class %}
|
|
133
|
+
{% capture anchorAttributes %}{{ anchorAttributes }} class="{{ include.anchor_class | split: '.' | join: ' ' }}"{% endcapture %}
|
|
134
|
+
{% endif %}
|
|
135
|
+
|
|
136
|
+
{% capture listItem %}<a{{ anchorAttributes }}>{{ anchorBody }}</a>{% endcapture %}
|
|
137
|
+
{% elsif skipNoIDs == true %}
|
|
138
|
+
{% continue %}
|
|
139
|
+
{% else %}
|
|
140
|
+
{% capture listItem %}{{ anchorBody }}{% endcapture %}
|
|
141
|
+
{% endif %}
|
|
142
|
+
|
|
143
|
+
{% if currLevel > lastLevel and flatToc == false %}
|
|
144
|
+
{% capture jekyll_toc %}{{ jekyll_toc }}<{{ listModifier }}{{ subMenuClass }}>{% endcapture %}
|
|
145
|
+
{% elsif currLevel < lastLevel and flatToc == false %}
|
|
146
|
+
{% assign repeatCount = lastLevel | minus: currLevel %}
|
|
147
|
+
|
|
148
|
+
{% for i in (1..repeatCount) %}
|
|
149
|
+
{% capture jekyll_toc %}{{ jekyll_toc }}</li></{{ listModifier }}>{% endcapture %}
|
|
150
|
+
{% endfor %}
|
|
151
|
+
|
|
152
|
+
{% capture jekyll_toc %}{{ jekyll_toc }}</li>{% endcapture %}
|
|
153
|
+
{% else %}
|
|
154
|
+
{% capture jekyll_toc %}{{ jekyll_toc }}</li>{% endcapture %}
|
|
155
|
+
{% endif %}
|
|
156
|
+
|
|
157
|
+
{% capture jekyll_toc %}{{ jekyll_toc }}<li{{ listItemClass }}>{{ listItem }}{% endcapture %}
|
|
158
|
+
|
|
159
|
+
{% assign lastLevel = currLevel %}
|
|
160
|
+
{% assign firstHeader = false %}
|
|
161
|
+
{% endfor %}
|
|
162
|
+
|
|
163
|
+
{% if flatToc == true %}
|
|
164
|
+
{% assign repeatCount = 1 %}
|
|
165
|
+
{% else %}
|
|
166
|
+
{% assign repeatCount = minHeader | minus: 1 %}
|
|
167
|
+
{% assign repeatCount = lastLevel | minus: repeatCount %}
|
|
168
|
+
{% endif %}
|
|
169
|
+
|
|
170
|
+
{% for i in (1..repeatCount) %}
|
|
171
|
+
{% capture jekyll_toc %}{{ jekyll_toc }}</li></{{ listModifier }}>{% endcapture %}
|
|
172
|
+
{% endfor %}
|
|
173
|
+
|
|
174
|
+
{% if jekyll_toc != '' %}
|
|
175
|
+
{% assign rootAttributes = '' %}
|
|
176
|
+
{% if include.class and include.class != blank %}
|
|
177
|
+
{% capture rootAttributes %} class="{{ include.class | split: '.' | join: ' ' }}"{% endcapture %}
|
|
178
|
+
{% endif %}
|
|
179
|
+
|
|
180
|
+
{% if include.id and include.id != blank %}
|
|
181
|
+
{% capture rootAttributes %}{{ rootAttributes }} id="{{ include.id }}"{% endcapture %}
|
|
182
|
+
{% endif %}
|
|
183
|
+
|
|
184
|
+
{% if rootAttributes %}
|
|
185
|
+
{% assign nodes = jekyll_toc | split: '>' %}
|
|
186
|
+
{% capture jekyll_toc %}<{{ listModifier }}{{ rootAttributes }}>{{ nodes | shift | join: '>' }}>{% endcapture %}
|
|
187
|
+
{% endif %}
|
|
188
|
+
{% endif %}
|
|
189
|
+
{% endcapture %}{% assign tocWorkspace = '' %}{{ deprecation_warnings }}{{ jekyll_toc -}}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
{% include head.html %}
|
|
3
|
+
|
|
4
|
+
<body
|
|
5
|
+
class="dark:text-primary-300 text-primary-700 dark:bg-primary-900 bg-primary-100 font-display flex flex-col items-center md:items-center lg:items-center">
|
|
6
|
+
<header
|
|
7
|
+
class="w-full dark:text-primary-700 text-primary-800 dark:bg-primary-950 bg-primary-300 flex flex-col items-center py-4 px-4 border-t-[6px] border-solid border-primary-500">
|
|
8
|
+
<div class="max-w-[65ch] w-full">
|
|
9
|
+
{% include nav.html %}
|
|
10
|
+
</div>
|
|
11
|
+
</header>
|
|
12
|
+
<div class="prose dark:prose-invert my-8 px-4 max-w-[65ch] w-full space-y-5">
|
|
13
|
+
{% include anchor_headings.html html=content anchorBody="<i class=\"fas fa-anchor fa-2xs hover:fa-bounce\"></i>" anchorClass="content-center" %}
|
|
14
|
+
</div>
|
|
15
|
+
<footer
|
|
16
|
+
class="w-full dark:text-primary-400 text-primary-800 dark:bg-primary-950 bg-primary-200 py-6 flex flex-col items-center">
|
|
17
|
+
<div class="max-w-[70ch] w-full">
|
|
18
|
+
{% include foot.html %}
|
|
19
|
+
</div>
|
|
20
|
+
</footer>
|
|
21
|
+
<script src="{{ '/assets/js/toggle-theme.js' | relative_url }}"></script>
|
|
22
|
+
</body>
|
|
23
|
+
|
|
24
|
+
</html>
|
data/_layouts/page.html
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<h1 class="mb-3">/{{ page.name | replace: '.md', '' | replace: '.html', '' }}</h1>
|
|
6
|
+
|
|
7
|
+
<div class="mb-6">
|
|
8
|
+
{% if page.toc == true %}
|
|
9
|
+
{% include toc-card.html html=content %}
|
|
10
|
+
{% endif %}
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
{{ content }}
|
|
14
|
+
|
|
15
|
+
<script src="{{ '/assets/js/scrollspy.js' | relative_url }}"></script>
|
data/_layouts/post.html
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<h1>{{ page.title }}</h1>
|
|
6
|
+
<p>{{ page.description }}</p>
|
|
7
|
+
|
|
8
|
+
<div class="flex flex-row gap-4">
|
|
9
|
+
<p class="my-0">On {{ page.date | date_to_string }}, by</p>
|
|
10
|
+
<a href="https://github.com/{{ page.author }}" class="flex flex-row items-center gap-2">
|
|
11
|
+
<img src="https://github.com/{{ page.author }}.png?size=30" class="my-0 w-8 h-8 rounded-full" />
|
|
12
|
+
@{{ page.author }}
|
|
13
|
+
</a>
|
|
14
|
+
</div>
|
|
15
|
+
{% if page.toc != false %}
|
|
16
|
+
{% include toc-card.html html=content %}
|
|
17
|
+
{% endif %}
|
|
18
|
+
<div>
|
|
19
|
+
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
{{ content }}
|
|
23
|
+
|
|
24
|
+
<script src="{{ '/assets/js/scrollspy.js' | relative_url }}"></script>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<h1>{{ page.title }}</h1>
|
|
6
|
+
<p>{{ page.description }}</p>
|
|
7
|
+
<div class="rounded-md bg-primary-300 dark:bg-primary-800 p-4 mb-4">
|
|
8
|
+
<ul class="my-0">
|
|
9
|
+
<li>
|
|
10
|
+
<div class="flex flex-row gap-2 items-center">
|
|
11
|
+
<p class="my-0 mr-1">Skills: </p>
|
|
12
|
+
{% for skill in page.skills %}
|
|
13
|
+
<div
|
|
14
|
+
class="bg-primary-500 dark:bg-primary-900 text-sm py-1 text-primary-200 dark:text-primary-400 items-center px-2 rounded-md flex flex-row gap-2 h-fit">
|
|
15
|
+
<i class="{{ skill.icon }}"></i>{{ skill.name }}
|
|
16
|
+
</div>
|
|
17
|
+
{% endfor %}
|
|
18
|
+
</div>
|
|
19
|
+
</li>
|
|
20
|
+
|
|
21
|
+
<li>
|
|
22
|
+
<div class="flex flex-row items-center gap-3"><i class="fas fa-link"></i><a href="{{ page.website }}">Visit
|
|
23
|
+
the website</a></div>
|
|
24
|
+
</li>
|
|
25
|
+
|
|
26
|
+
<li>
|
|
27
|
+
<div class="flex flex-row items-center gap-3"><i class="fa-brands fa-youtube"></i><a
|
|
28
|
+
href="{{ page.video }}">Watch the recap</a></div>
|
|
29
|
+
</li>
|
|
30
|
+
</ul>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
{{ content }}
|
data/assets/css/main.css
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
---
|
|
3
|
+
|
|
4
|
+
@import "tailwindcss/base";
|
|
5
|
+
@import "tailwindcss/components";
|
|
6
|
+
@import "tailwindcss/utilities";
|
|
7
|
+
|
|
8
|
+
@layer base {
|
|
9
|
+
@font-face {
|
|
10
|
+
font-family: 'LibertinusMonoMono';
|
|
11
|
+
src: url('../fonts/LibertinusMonoMono.otf') format('opentype');
|
|
12
|
+
font-weight: normal;
|
|
13
|
+
font-style: normal;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@layer components {
|
|
18
|
+
a {
|
|
19
|
+
@apply dark:!text-primary-400 !text-primary-800 !underline !decoration-dotted;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
h1 {
|
|
23
|
+
font-family: sans-serif
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
a[href^="http://"]:after,
|
|
27
|
+
a[href^="https://"]:after {
|
|
28
|
+
content: "\f08e";
|
|
29
|
+
font-family: FontAwesome;
|
|
30
|
+
font-weight: normal;
|
|
31
|
+
font-style: normal;
|
|
32
|
+
display: inline-block;
|
|
33
|
+
margin-left: 3px;
|
|
34
|
+
text-decoration: none;
|
|
35
|
+
padding-left: 3px;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
body {
|
|
39
|
+
text-underline-offset: 4px;
|
|
40
|
+
counter-reset: sidenote-counter;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
small {
|
|
44
|
+
@apply hidden mr-[-35%] xl:mr-[-50%] lg:block;
|
|
45
|
+
counter-increment: sidenote-counter;
|
|
46
|
+
float: right;
|
|
47
|
+
clear: right;
|
|
48
|
+
width: 40%;
|
|
49
|
+
margin-top: 1em;
|
|
50
|
+
margin-bottom: 1em;
|
|
51
|
+
font-size: 1.0rem;
|
|
52
|
+
line-height: 1.3;
|
|
53
|
+
vertical-align: baseline;
|
|
54
|
+
position: relative;
|
|
55
|
+
padding: 1em;
|
|
56
|
+
border-top: 1px dotted #CCCCCC;
|
|
57
|
+
border-bottom: 1px dotted #CCCCCC;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
small::before {
|
|
61
|
+
@apply border-primary-500 rounded-full mr-2 mt-1 p-1 w-8 h-8 items-center text-center;
|
|
62
|
+
content: counter(sidenote-counter);
|
|
63
|
+
float: left;
|
|
64
|
+
border-style: solid;
|
|
65
|
+
border-width: 2px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.prose>h2,
|
|
69
|
+
h3 {
|
|
70
|
+
gap: 8px;
|
|
71
|
+
display: flex;
|
|
72
|
+
}
|
|
73
|
+
li::marker {
|
|
74
|
+
@apply !text-primary-500;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TOC ScrollSpy
|
|
3
|
+
* Highlights the current section in the table of contents
|
|
4
|
+
*/
|
|
5
|
+
function initTocScrollSpy() {
|
|
6
|
+
const headings = document.querySelectorAll('h2[id], h3[id], h4[id], h5[id], h6[id]');
|
|
7
|
+
const tocLinks = document.querySelectorAll('.toc-link');
|
|
8
|
+
|
|
9
|
+
if (headings.length === 0 || tocLinks.length === 0) return;
|
|
10
|
+
|
|
11
|
+
const idToTocLink = {};
|
|
12
|
+
tocLinks.forEach(link => {
|
|
13
|
+
const href = link.getAttribute('href');
|
|
14
|
+
if (href && href.startsWith('#')) {
|
|
15
|
+
idToTocLink[href.substring(1)] = link;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const headingPositions = Array.from(headings).map(heading => {
|
|
20
|
+
return {
|
|
21
|
+
id: heading.id,
|
|
22
|
+
top: heading.getBoundingClientRect().top + window.pageYOffset - 100
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
function updateToc() {
|
|
27
|
+
const scrollPosition = window.pageYOffset;
|
|
28
|
+
|
|
29
|
+
let currentHeadingIndex = headingPositions.findIndex(heading =>
|
|
30
|
+
heading.top > scrollPosition
|
|
31
|
+
) - 1;
|
|
32
|
+
|
|
33
|
+
if (currentHeadingIndex === -2) {
|
|
34
|
+
currentHeadingIndex = headingPositions.length - 1;
|
|
35
|
+
} else if (currentHeadingIndex < 0) {
|
|
36
|
+
currentHeadingIndex = 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
tocLinks.forEach(link => link.classList.remove('font-bold', 'text-primary-800', 'dark:text-primary-300'));
|
|
40
|
+
|
|
41
|
+
const currentId = headingPositions[currentHeadingIndex]?.id;
|
|
42
|
+
if (currentId && idToTocLink[currentId]) {
|
|
43
|
+
idToTocLink[currentId].classList.add('font-bold', 'text-primary-800', 'dark:text-primary-300');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
updateToc();
|
|
48
|
+
|
|
49
|
+
let ticking = false;
|
|
50
|
+
window.addEventListener('scroll', function() {
|
|
51
|
+
if (!ticking) {
|
|
52
|
+
window.requestAnimationFrame(function() {
|
|
53
|
+
updateToc();
|
|
54
|
+
ticking = false;
|
|
55
|
+
});
|
|
56
|
+
ticking = true;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
document.addEventListener('DOMContentLoaded', initTocScrollSpy);
|
|
62
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
document.getElementById('theme-toggle').addEventListener('click', (event) => {
|
|
2
|
+
let newTheme = undefined;
|
|
3
|
+
if (localStorage.theme === "dark") {
|
|
4
|
+
localStorage.removeItem('theme')
|
|
5
|
+
} else if (!localStorage.theme) {
|
|
6
|
+
newTheme = 'light';
|
|
7
|
+
localStorage.theme = 'light';
|
|
8
|
+
} else {
|
|
9
|
+
newTheme = 'dark';
|
|
10
|
+
localStorage.theme = 'dark';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
applyTheme(newTheme);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
document.getElementById('theme-dropdown').addEventListener('change', (event) => {
|
|
17
|
+
if (event.target.dataset.changed ) {
|
|
18
|
+
event.target.dataset.changed = undefined;
|
|
19
|
+
if (new Date(event.target.dataset.changed) - new Date() < 1000) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const selectedTheme = event.target.value;
|
|
24
|
+
if (selectedTheme === 'light' || selectedTheme === 'dark') {
|
|
25
|
+
localStorage.theme = selectedTheme;
|
|
26
|
+
} else {
|
|
27
|
+
selectedTheme = undefined;
|
|
28
|
+
localStorage.removeItem('theme');
|
|
29
|
+
}
|
|
30
|
+
applyTheme(selectedTheme, true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
applyTheme();
|
data/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"dependencies": {
|
|
3
|
+
"autoprefixer": "^10.4.21",
|
|
4
|
+
"postcss": "^8.5.3",
|
|
5
|
+
"postcss-import": "^14.1.0"
|
|
6
|
+
},
|
|
7
|
+
"devDependencies": {
|
|
8
|
+
"@tailwindcss/typography": "^0.5.16",
|
|
9
|
+
"cssnano": "^5.1.15",
|
|
10
|
+
"tailwindcss": "^3.4.17"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"postinstall": "bundle install",
|
|
14
|
+
"dev": "bundle exec jekyll serve --watch",
|
|
15
|
+
"build": "python utils/postMarkdown.py && bundle exec jekyll build",
|
|
16
|
+
"clean": "bundle exec jekyll clean",
|
|
17
|
+
"doctor": "bundle exec jekyll doctor",
|
|
18
|
+
"post": "python utils/postMarkdown.py"
|
|
19
|
+
}
|
|
20
|
+
}
|
data/postcss.config.js
ADDED
data/tailwind.config.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import colors from 'tailwindcss/colors'
|
|
2
|
+
|
|
3
|
+
/** @type {import('tailwindcss').Config} */
|
|
4
|
+
module.exports = {
|
|
5
|
+
darkMode: 'selector',
|
|
6
|
+
content: [
|
|
7
|
+
'./_includes/**/*.html',
|
|
8
|
+
'./_layouts/**/*.html',
|
|
9
|
+
'./_posts/*.md',
|
|
10
|
+
'./*.html',
|
|
11
|
+
],
|
|
12
|
+
theme: {
|
|
13
|
+
extend: {
|
|
14
|
+
colors: {
|
|
15
|
+
primary: colors.stone,
|
|
16
|
+
},
|
|
17
|
+
typography: {
|
|
18
|
+
DEFAULT: {
|
|
19
|
+
css: (theme) => ({
|
|
20
|
+
a: {
|
|
21
|
+
color: theme('colors.primary.200') + ' !important',
|
|
22
|
+
'text-decoration': 'underline !important',
|
|
23
|
+
'text-decoration-style': 'dotted !important',
|
|
24
|
+
'&:hover': {
|
|
25
|
+
color: theme('colors.primary.400') + ' !important',
|
|
26
|
+
},
|
|
27
|
+
'@screen dark': {
|
|
28
|
+
color: theme('colors.primary.400') + ' !important',
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
}),
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
fontFamily: {
|
|
36
|
+
display: ['LibertinusMonoMono'],
|
|
37
|
+
sans: ['sans-serif'],
|
|
38
|
+
icon: ['FontAwesome'],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
variants: {},
|
|
42
|
+
plugins: [require('@tailwindcss/typography')],
|
|
43
|
+
}
|
metadata
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: jekyll-theme-doctored
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.3
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- matmanna
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2025-11-06 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: jekyll
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 3.9.0
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 3.9.0
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: jekyll-postcss
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 0.5.0
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 0.5.0
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: bundler
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
54
|
+
description: " Doctored is a jekyll theme built with tailwind CSS which enables the
|
|
55
|
+
ceration of static, yet highly advanced, blogs, digital gardens, documentation,
|
|
56
|
+
and portfolio websites."
|
|
57
|
+
email:
|
|
58
|
+
- ''
|
|
59
|
+
executables: []
|
|
60
|
+
extensions: []
|
|
61
|
+
extra_rdoc_files: []
|
|
62
|
+
files:
|
|
63
|
+
- LICENSE
|
|
64
|
+
- README.md
|
|
65
|
+
- _config.yml
|
|
66
|
+
- _includes/anchor_headings.html
|
|
67
|
+
- _includes/foot.html
|
|
68
|
+
- _includes/head.html
|
|
69
|
+
- _includes/nav.html
|
|
70
|
+
- _includes/toc-card.html
|
|
71
|
+
- _includes/toc.html
|
|
72
|
+
- _layouts/default.html
|
|
73
|
+
- _layouts/page.html
|
|
74
|
+
- _layouts/post.html
|
|
75
|
+
- _layouts/project.html
|
|
76
|
+
- assets/css/main.css
|
|
77
|
+
- assets/fonts/LibertinusMonoMono.otf
|
|
78
|
+
- assets/images/favicon.ico
|
|
79
|
+
- assets/images/favicon.png
|
|
80
|
+
- assets/js/scrollspy.js
|
|
81
|
+
- assets/js/toggle-theme.js
|
|
82
|
+
- package.json
|
|
83
|
+
- postcss.config.js
|
|
84
|
+
- tailwind.config.js
|
|
85
|
+
homepage: https://matmanna.github.io/doctored
|
|
86
|
+
licenses:
|
|
87
|
+
- MIT
|
|
88
|
+
metadata:
|
|
89
|
+
homepage_uri: https://matmanna.github.io/doctored
|
|
90
|
+
source_code_uri: https://github.com/https://github.com/matmanna/doctored
|
|
91
|
+
rdoc_options: []
|
|
92
|
+
require_paths:
|
|
93
|
+
- lib
|
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
95
|
+
requirements:
|
|
96
|
+
- - ">="
|
|
97
|
+
- !ruby/object:Gem::Version
|
|
98
|
+
version: '0'
|
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
requirements: []
|
|
105
|
+
rubygems_version: 3.6.2
|
|
106
|
+
specification_version: 4
|
|
107
|
+
summary: "\U0001F3A9 A blog theme prioritizing uniqueness, complexity, and sophistication."
|
|
108
|
+
test_files: []
|