mirror of
https://github.com/Clortox/tylerperkins.xyz.git
synced 2026-05-01 11:27:59 +00:00
Compare commits
7 Commits
claude-par
...
758c0cde84
| Author | SHA1 | Date | |
|---|---|---|---|
|
758c0cde84
|
|||
|
87159e568a
|
|||
|
643068c4ef
|
|||
|
db3fa322b1
|
|||
|
b7d64dab09
|
|||
|
|
0d0a878fa7 | ||
|
7a28b1dc71
|
49
README.md
49
README.md
@@ -5,3 +5,52 @@ This repo contains the [Hugo](https://gohugo.io) source for my personal website,
|
||||
Find the active source of this repository on my [Gitea](https://git.clortox.com/Infrastructure/tylerperkins.xyz), where I make issues to track additions, and am more active to pull requests.
|
||||
|
||||
Find the mirror of this repository on [Github](https://github.com/Clortox/tylerperkins.xyz).
|
||||
|
||||
## Writing Posts
|
||||
|
||||
### Sidenotes / Margin Notes
|
||||
|
||||
This blog supports Tufte-style sidenotes that appear in the right margin on desktop and at the bottom of posts on mobile. To add a sidenote:
|
||||
|
||||
```markdown
|
||||
This is some text{{< sidenote >}}This is a sidenote that appears in the margin{{< /sidenote >}} with more text following.
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- On desktop (≥1200px): Notes appear as cards in the right margin, aligned with where they're referenced
|
||||
- On mobile (<1200px): Notes appear in a "Notes" section at the end of the post
|
||||
- Automatic numbering: `[0]`, `[1]`, `[2]`, etc.
|
||||
- Bidirectional links: Click the number in text to jump to the note, click the number in the note to jump back
|
||||
- Supports markdown and math: `$$x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$$`
|
||||
|
||||
### Summary Breaks
|
||||
|
||||
Control what appears on the homepage and list pages using the `<!--more-->` separator:
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: "My Post Title"
|
||||
date: 2026-01-16
|
||||
---
|
||||
|
||||
This is the introduction paragraph that will appear on list pages as the summary.
|
||||
|
||||
<!--more-->
|
||||
|
||||
Everything after this line only appears on the full post page.
|
||||
```
|
||||
|
||||
Without `<!--more-->`, Hugo will automatically create a summary from the first ~70 words.
|
||||
|
||||
### Clickable Headings
|
||||
|
||||
All headings automatically get anchor links. Hover over any heading to see a `#` symbol appear. Click it to get a direct link to that section.
|
||||
|
||||
### Markdown Source Downloads
|
||||
|
||||
Every post automatically generates a downloadable markdown source file. At the bottom of each post page, you'll find a "Download Markdown" link that provides the original markdown source, including all shortcodes and formatting.
|
||||
|
||||
**Technical Details:**
|
||||
- Markdown files are generated as `index.md` alongside `index.html` in each post directory
|
||||
- The raw content includes front matter, shortcodes, and all original formatting
|
||||
- This is configured via Hugo's custom output formats in `config.yaml`
|
||||
|
||||
38
config.yaml
38
config.yaml
@@ -1,21 +1,19 @@
|
||||
baseURL: 'http://tylerperkins.xyx'
|
||||
baseURL: 'http://tylerperkins.xyz'
|
||||
languageCode: 'en-us'
|
||||
LanguageCode: 'en-us'
|
||||
title: 'Tylers Website'
|
||||
theme: "clortox"
|
||||
markup:
|
||||
goldmark:
|
||||
extensions:
|
||||
passthrough:
|
||||
delimiters:
|
||||
block:
|
||||
- - \[
|
||||
- \]
|
||||
- - $$
|
||||
- $$
|
||||
inline:
|
||||
- - \(
|
||||
- \)
|
||||
enable: true
|
||||
params:
|
||||
math: true
|
||||
title: "Tyler Perkins"
|
||||
|
||||
outputs:
|
||||
page:
|
||||
- HTML
|
||||
- markdown
|
||||
|
||||
outputFormats:
|
||||
markdown:
|
||||
mediaType: "text/markdown"
|
||||
isPlainText: true
|
||||
isHTML: false
|
||||
permalinkable: false
|
||||
name: "markdown"
|
||||
path: ""
|
||||
baseName: "index"
|
||||
rel: "alternate"
|
||||
|
||||
@@ -3,7 +3,10 @@ title: "Home"
|
||||
date: 2023-06-27
|
||||
---
|
||||
|
||||
Software engineer, aspiring polymath. I like to do a little bit of everything.
|
||||
|
||||
# Hi, I'm Tyler
|
||||
Interested in proving correctness, embedded systems, system design, genetic engineering, microscopy,
|
||||
mechanical engineering, pure math (mostly algebra, category theory, number theory), operating systems design,
|
||||
security, and more.
|
||||
|
||||
software engineer, aspiring postmath
|
||||
Open to research work and projects ([email me](mailto:hello@clortox.com))
|
||||
|
||||
27
content/posts/catching-up/index.md
Normal file
27
content/posts/catching-up/index.md
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
title: "Catching Up"
|
||||
date: 2026-01-30T13:30:00-04:00
|
||||
draft: false
|
||||
---
|
||||
|
||||
Hi. Its been quite a while since I wrote here.
|
||||
|
||||
I started writing here as an exercise in the Feynman technique. For example, the vector clock post was made when I started my masters; my first class was distributed systems.
|
||||
Last year I set a goal for myself to post on here once a week, and you can see for yourself how well that went.
|
||||
|
||||
At that point in my life, especially 2024, I had this attitude of needing to optimize. Writing was something that I wanted to do to get more out of myself.
|
||||
Between a sense of burnout
|
||||
{{< sidenote >}}Does anyone else experience the feeling of being overwhelmed at all they do, yet at the same time feel as though they have the potential to do so much more?{{< /sidenote >}},
|
||||
and personal events, I have relaxed from this attitude.
|
||||
|
||||
I'm now more of a 'humanist'
|
||||
{{< sidenote >}}I'm sure there is a real word for what I'm describing that I don't know{{< /sidenote >}}
|
||||
now than I was before. I place more emphasis on the journey, and the emotional feedback I feel in the moment to things I do.
|
||||
That's not to say I want to give in to the basic desires in the moment, as then I would never get anything done other than sitting playing games, watching youtube, and working on silly puzzles.
|
||||
Rather I'm more aware of when I feel exhaustion, and listen to it rather than chastise myself, believing if I was a little stronger, I would be able to push through.
|
||||
|
||||
Part of me still longs for that feeling, as it was during that time that I learned the most at my job. I feel that has slowed down. I'm still struggling to come to terms with that,
|
||||
and am still looking for ways to get more out of myself while preserving a 'humanist' mindset.
|
||||
|
||||
All of this to say, I'm attempting to write here more. But at my own pace. I hope to write to you soon, hopefully something more technical.
|
||||
|
||||
126
content/posts/spaces-as-models/index.md
Normal file
126
content/posts/spaces-as-models/index.md
Normal file
@@ -0,0 +1,126 @@
|
||||
---
|
||||
title: "Spaces as Models, and the shittiest JSON validation algorithm"
|
||||
date: 2026-04-17T12:00:00-04:00
|
||||
draft: false
|
||||
---
|
||||
|
||||
Today I would like to talk about a concept I have come to rely on quite a lot,
|
||||
especially over the past two or three years.
|
||||
|
||||
Spaces, or state spaces, are a conceptual tool for understanding the world.
|
||||
Boiled down, they are the practice of imagining all configurations/solutions
|
||||
of a problem as points in a space,
|
||||
and variables to your problem as dimensions in that space.
|
||||
We from here can assign structures that are useful, such as a topology
|
||||
{{<sidenote>}}A topology is a structure over a set where we decide what items in that set are in the same "neighborhood". This lets us get concepts about continuity without needing specific distances. You can roughly think about it as defining things that are "close". {{</sidenote>}},
|
||||
algebra
|
||||
{{<sidenote>}}A set where we define one or more binary operators over the elements of the set, that follow some set of rules, such as groups, rings, etc{{</sidenote>}},
|
||||
or morphisms between spaces
|
||||
{{<sidenote>}}A mapping between two spaces, usually one that preserves some useful property. It can be thought of as a formal way of defining "analogies" between problem spaces.{{</sidenote>}}.
|
||||
From this we have some way to think about all "solutions" or states that our
|
||||
problem can be in.
|
||||
|
||||
## Example
|
||||
|
||||
One of the most famous examples of space modeling is the Mandelbrot set.
|
||||
The function here we are modeling is
|
||||
|
||||
$$
|
||||
f_c(z) = z^2 + c
|
||||
$$
|
||||
|
||||
where c is a complex number. The graph below is the complex plane, or the entire domain of c.
|
||||
z is the value being iterated, starting at zero.
|
||||
The coloring here denotes if the series below "blows up", or
|
||||
approaches infinity.
|
||||
|
||||
$$
|
||||
|f_c(0)|, |f_c(f_c(0))|, ...
|
||||
$$
|
||||
|
||||

|
||||
|
||||
Mandelbrot set{{<sidenote>}}By Created by Wolfgang Beyer with the program Ultra Fractal 3. - Own work, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=321973{{</sidenote>}}
|
||||
|
||||
Here we are modeling the "blow up" behaviour in a space, namely the space of all complex numbers.
|
||||
Even without the visualization, you might have instinctively reached for a similar kind of spatial thinking.
|
||||
This is such a natural way of modeling these types of problems because it's so useful.
|
||||
A large amount of high-school algebra is teaching students how to think about problems in this way.
|
||||
|
||||
## Less obvious example
|
||||
|
||||
Take a JSON parser. You may write its declaration as something like,
|
||||
|
||||
```python
|
||||
def parseJson(input: str) -> dict:
|
||||
# implementation
|
||||
```
|
||||
|
||||
This works, but you have to have cases to deal with inputs that are strings, but are not valid JSON. For example, the input "qwertyadslgkjafgjlkj" or any other random string
|
||||
will not parse, and therefore you will have to throw an exception. How can we avoid this, or at least model this?
|
||||
|
||||
We can think of all possible finite strings
|
||||
{{<sidenote>}}This structure is called a [Free Monoid](https://en.wikipedia.org/wiki/Free_monoid){{</sidenote>}}.
|
||||
as nodes in a graph, and the connections between the points in our graph
|
||||
being single edits. So the node representing the string "Hello" would have edges connecting to "Hello!", "Hell", and "hello".
|
||||
{{<sidenote>}}This is not all nodes "Hello" would connect to, just a few examples to illustrate the point{{</sidenote>}}
|
||||
This hop makes the "distance" one
|
||||
{{<sidenote>}}This distance is called [Levenshtein Distance](https://en.wikipedia.org/wiki/Levenshtein_distance) between two strings{{</sidenote>}}
|
||||
|
||||
Knowing this graph represents all possible values `input` can take on above, we know there is some sub-graph
|
||||
that contains all valid JSON strings (because JSON strings are strings, and therefore must be in the set of all strings).
|
||||
|
||||
There is a structure, or test, to determine if a string is part of this sub-graph, namely if it follows the grammar defined in
|
||||
[RFC 4627](https://www.ietf.org/rfc/rfc4627.txt). From this we could define a new type, `JSONString`, which is a subset of all strings,
|
||||
that follows this structure.
|
||||
|
||||
Why is `JSONString` useful? Didn't we just make a wrapper around string? Yes, but now `parseJson`, whose job
|
||||
is to parse JSON, does not need to worry about parsing something that is not JSON, namely any old string not in
|
||||
the space of all JSON strings. Now that responsibility is on `JSONString`, which is the boundary of our system.
|
||||
This lets us throw errors fast, before we waste time getting to the step of trying to parse `input`.
|
||||
|
||||
### On the structure of the graph of all JSON Strings
|
||||
|
||||
On implementing `JSONString`, how can we make use of the structure prescribed before? If the sub-graph of
|
||||
all JSON strings is connected (we can walk from any JSON string to any other JSON string,
|
||||
by adding, subtracting, or modifying a single character at a time),
|
||||
then our implementation might be able to start at some known JSON object, like `{}`, and walk to the provided `JSONString`.
|
||||
If we can walk to it on this graph, then we just proved it's JSON. Failing to find a path however is not proof that the provided
|
||||
string isn't JSON, just that we couldn't find a path in our search bounds (time, steps, etc).
|
||||
Of course, this is astronomically impractical. It's far cheaper to just validate the grammar of the string.
|
||||
Regardless, I think it's a neat property to know about, even if it does not have an immediate application to this problem.
|
||||
|
||||
Is the graph of all JSON strings connected? I have a hunch, however I leave exploring that to the reader.
|
||||
|
||||
### On morphisms
|
||||
|
||||
Notice that in modeling `parseJson` we have defined a morphism between the space of all JSON strings
|
||||
and a space of python values, such as lists, dicts, strings, numbers, and bools. We can think of `parseJson` as taking the space of JSON strings and mapping
|
||||
it to a space of python values
|
||||
{{<sidenote>}}Not the space of all python values, just those that can be represented by JSON.{{</sidenote>}}.
|
||||
|
||||
In some problems, this way of thinking can really make more obvious how a method (that often models the morphism)
|
||||
should be implemented. For example, seeing that a morphism is a projection, collapsing some higher-dimension domain into a lower-dimensional range,
|
||||
or an embedding, raising some lower-dimensional data into a higher dimension with more structure/detail, the implementation will often naturally follow.
|
||||
|
||||
## Theory on why
|
||||
|
||||
I have a personal theory on why we are so spatially inclined.
|
||||
{{<sidenote>}}Based on nothing but my gut, however I'm sure there is research I'm not aware of that may back this.{{</sidenote>}}
|
||||
We struggle to memorize a list of over ten numbers,
|
||||
but can remember how to drive to tens to hundreds of places without issue.
|
||||
This is such a useful property there are techniques dedicated to exploiting this property of our cognition
|
||||
{{<sidenote>}}See [memory of loci](https://en.wikipedia.org/wiki/Method_of_loci), or generally the concept of "memory castles"{{</sidenote>}}.
|
||||
Spatial modeling seems to be such an innate feature we are built for, which likely explains why this technique
|
||||
feels so natural after some practice.
|
||||
|
||||
## Conclusion
|
||||
|
||||
The two examples I've provided above of viewing problems as spaces are just the beginning.
|
||||
We've demonstrated this idea on both the continuous and discrete problems. This line of thinking
|
||||
applies to more than just programmers; anything can be modeled as spaces, from the space
|
||||
of all possible negotiations in a deal, to the internal state of some classes
|
||||
of machine learning models (latent spaces), to the space of all routes you can take to drive home.
|
||||
|
||||
When in doubt on a problem, try thinking spatially; you may be surprised at what insights you can glean,
|
||||
even if the best idea it gives you is the shittiest JSON validator ever written.
|
||||
BIN
content/posts/spaces-as-models/mandelbrot.jpg
Normal file
BIN
content/posts/spaces-as-models/mandelbrot.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
@@ -1,32 +0,0 @@
|
||||
{
|
||||
"Contact": [
|
||||
{
|
||||
"name": "Email",
|
||||
"url": "mailto:hello@clortox.com"
|
||||
},
|
||||
{
|
||||
"name": "LinkedIn",
|
||||
"url": "https://www.linkedin.com/in/tyler-perkins-xyz/"
|
||||
},
|
||||
{
|
||||
"name": "Github",
|
||||
"url": "https://github.com/Clortox"
|
||||
}
|
||||
],
|
||||
"Services": [
|
||||
{
|
||||
"name": "Git",
|
||||
"url": "https://git.clortox.com/"
|
||||
},
|
||||
{
|
||||
"name": "Files",
|
||||
"url": "https://files.clortox.com/"
|
||||
},
|
||||
{
|
||||
"name": "Youtube Frontend",
|
||||
"url": "https://watch.clortox.com/"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"LeftHandSide": [
|
||||
{
|
||||
"name": "Home",
|
||||
"url": "/"
|
||||
},
|
||||
{
|
||||
"name": "Resume",
|
||||
"url": "/resume.pdf"
|
||||
},
|
||||
{
|
||||
"name": "Posts",
|
||||
"url": "/posts/"
|
||||
}
|
||||
],
|
||||
"RightHandSide": [
|
||||
{
|
||||
"name": "PGP Key",
|
||||
"url": "/me.gpg"
|
||||
},
|
||||
{
|
||||
"name": "User Portal",
|
||||
"url": "https://auth.clortox.com/"
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
{
|
||||
"About": {
|
||||
"Content": [
|
||||
{
|
||||
"value": "Software developer with a passion for learning everything."
|
||||
},
|
||||
{
|
||||
"value": "Employed as a full stack developer at Etactics Inc, with a focus on back end development and system architecture."
|
||||
},
|
||||
{
|
||||
"value": "Aspiring polymath; Jack of all trades, master of few."
|
||||
},
|
||||
{
|
||||
"value": "Special interest in software design and architecture, pure math, and all forms of engineering."
|
||||
},
|
||||
{
|
||||
"value": "Currently persuing a Masters in Computer Science at Georgia Tech."
|
||||
}
|
||||
],
|
||||
"Interests": [
|
||||
{
|
||||
"value": "Software Architecture"
|
||||
},
|
||||
{
|
||||
"value": "Embedded Systems"
|
||||
},
|
||||
{
|
||||
"value": "Computer Engineering"
|
||||
},
|
||||
{
|
||||
"value": "Mechanical Engineering"
|
||||
},
|
||||
{
|
||||
"value": "Control Systems"
|
||||
},
|
||||
{
|
||||
"value": "Fabrication (Welding, Woodworking, 3D Printing, Laser Cutting)"
|
||||
},
|
||||
{
|
||||
"value": "Cars and Small Engines"
|
||||
},
|
||||
{
|
||||
"value": "Ham Radio (CQCQCQ KE8TIZ QRK?)"
|
||||
},
|
||||
{
|
||||
"value": "Chemistry"
|
||||
},
|
||||
{
|
||||
"value": "Mathematics"
|
||||
},
|
||||
{
|
||||
"value": "Firearms"
|
||||
},
|
||||
{
|
||||
"value": "Botany"
|
||||
},
|
||||
{
|
||||
"value": "Homesteading"
|
||||
},
|
||||
{
|
||||
"value": "Philosophy"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{{ define "main" }}
|
||||
<div class="centered top-fade" style="height:100vh">
|
||||
<div class="intro-content">
|
||||
<h1 data-value="command not found" data-show="error-message" class="typewriter"></h1>
|
||||
<div id="error-message">
|
||||
<p data-value="Page not found, sorry about that"></p>
|
||||
</div>
|
||||
<div class="link-block">
|
||||
<a href="javascript:history.back()"><-- Go back</a>
|
||||
<p> | </p>
|
||||
<a href="{{ .Site.BaseURL }}">Home --></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ end }}
|
||||
@@ -1,16 +0,0 @@
|
||||
{{ define "main" }}
|
||||
<div class="centered top-fade" style="height:100vh">
|
||||
<div class="intro-content">
|
||||
<h1 data-value="kernel panic!" data-show="error-message" class="typewriter"></h1>
|
||||
<div id="error-message">
|
||||
<p data-value="Error on our side, my bad"></p>
|
||||
</div>
|
||||
<div class="link-block">
|
||||
<a href="javascript:history.back()"><-- Go back</a>
|
||||
<p> | </p>
|
||||
<a href="{{ .Site.BaseURL }}">Home --></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ end }}
|
||||
5
layouts/_default/_markup/render-heading.html
Normal file
5
layouts/_default/_markup/render-heading.html
Normal file
@@ -0,0 +1,5 @@
|
||||
<h{{ .Level }} id="{{ .Anchor | safeURL }}">
|
||||
<a href="#{{ .Anchor | safeURL }}" class="heading-anchor">
|
||||
<span class="anchor-icon">#</span>{{ .Text | safeHTML }}
|
||||
</a>
|
||||
</h{{ .Level }}>
|
||||
@@ -1 +0,0 @@
|
||||
<center><img src="{{ .Destination }}" title="{{ with .Title }}{{ . }} {{ else }}{{ .Text }}{{ end }}" alt="{{ .Text }}" /></center>
|
||||
@@ -1 +0,0 @@
|
||||
<a href="{{ .Destination }}" title="{{ .Title }}" data-value="{{ .Text | safeHTML }}" class="scramble">{{ .Text | safeHTML }}</a>
|
||||
272
layouts/_default/baseof.html
Normal file
272
layouts/_default/baseof.html
Normal file
@@ -0,0 +1,272 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ if not .IsHome }}{{ .Title }} - {{ end }}{{ .Site.Title }}</title>
|
||||
<link rel="alternate" type="application/rss+xml" title="{{ .Site.Title }}" href="/index.xml">
|
||||
<style>
|
||||
:root {
|
||||
--bg-color: #fff;
|
||||
--text-color: #333;
|
||||
--link-color: #0066cc;
|
||||
--border-color: #ccc;
|
||||
--secondary-color: #666;
|
||||
--code-bg: #f4f4f4;
|
||||
--code-border: #ddd;
|
||||
}
|
||||
[data-theme="dark"] {
|
||||
--bg-color: #1a1a1a;
|
||||
--text-color: #e0e0e0;
|
||||
--link-color: #6ba3ff;
|
||||
--border-color: #444;
|
||||
--secondary-color: #999;
|
||||
--code-bg: #2d2d2d;
|
||||
--code-border: #444;
|
||||
--shadow: 0 1px 3px rgba(0,0,0,0.4);
|
||||
}
|
||||
:root {
|
||||
--shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
body {
|
||||
font-family: Georgia, serif;
|
||||
line-height: 1.6;
|
||||
max-width: 650px;
|
||||
margin: 40px auto;
|
||||
padding: 0 20px;
|
||||
color: var(--text-color);
|
||||
background: var(--bg-color);
|
||||
transition: background 0.3s, color 0.3s;
|
||||
}
|
||||
a {
|
||||
color: var(--link-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
h1, h2, h3 {
|
||||
line-height: 1.2;
|
||||
}
|
||||
header {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 30px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
header h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
}
|
||||
.header-right {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
nav a {
|
||||
font-size: 14px;
|
||||
}
|
||||
.theme-toggle {
|
||||
background: none;
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-color);
|
||||
cursor: pointer;
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.theme-toggle:hover {
|
||||
background: var(--code-bg);
|
||||
}
|
||||
footer {
|
||||
margin-top: 50px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid var(--border-color);
|
||||
font-size: 14px;
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
.date {
|
||||
color: var(--secondary-color);
|
||||
font-size: 14px;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
pre {
|
||||
background: var(--code-bg);
|
||||
border: 1px solid var(--code-border);
|
||||
padding: 10px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
code {
|
||||
background: var(--code-bg);
|
||||
padding: 2px 5px;
|
||||
}
|
||||
pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Heading anchors */
|
||||
.heading-anchor {
|
||||
color: var(--text-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
.heading-anchor:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.anchor-icon {
|
||||
opacity: 0;
|
||||
margin-right: 5px;
|
||||
color: var(--link-color);
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
.heading-anchor:hover .anchor-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Sidenotes/Margin notes */
|
||||
/* Hide sidenotes by default (except on single post pages) */
|
||||
.sidenote-container {
|
||||
display: none;
|
||||
}
|
||||
.post-content .sidenote-container {
|
||||
display: inline;
|
||||
}
|
||||
.sidenote-ref {
|
||||
color: var(--link-color);
|
||||
text-decoration: none;
|
||||
font-size: 0.85em;
|
||||
vertical-align: super;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
/* Mobile: sidenotes at the end */
|
||||
@media (max-width: 1199px) {
|
||||
.post-content .sidenote-container {
|
||||
display: inline;
|
||||
}
|
||||
.post-content .sidenote {
|
||||
display: none;
|
||||
}
|
||||
#footnotes-section {
|
||||
margin-top: 20px;
|
||||
}
|
||||
#footnotes-section .sidenote {
|
||||
display: block;
|
||||
position: static;
|
||||
margin-bottom: 15px;
|
||||
font-size: 0.85em;
|
||||
line-height: 1.4;
|
||||
padding: 10px;
|
||||
background: var(--code-bg);
|
||||
border-left: 2px solid var(--link-color);
|
||||
border-radius: 3px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
}
|
||||
|
||||
/* Desktop: sidenotes in right margin */
|
||||
@media (min-width: 1200px) {
|
||||
/* Only expand body width when sidenotes actually exist */
|
||||
body:has(.sidenote) {
|
||||
max-width: 1000px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.post-content {
|
||||
position: relative;
|
||||
}
|
||||
.post-content .content-wrapper {
|
||||
max-width: 650px;
|
||||
position: relative;
|
||||
}
|
||||
.post-content .sidenote-container {
|
||||
position: static;
|
||||
}
|
||||
.post-content .sidenote {
|
||||
position: absolute;
|
||||
left: 690px;
|
||||
width: 250px;
|
||||
font-size: 0.85em;
|
||||
line-height: 1.4;
|
||||
padding: 10px;
|
||||
margin-top: 0;
|
||||
background: var(--code-bg);
|
||||
border-left: 2px solid var(--link-color);
|
||||
border-radius: 3px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
#footnotes-section {
|
||||
display: none;
|
||||
}
|
||||
#sidenotes-column {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
// Theme toggle - runs immediately to prevent flash
|
||||
(function() {
|
||||
const theme = localStorage.getItem('theme') || 'light';
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
})();
|
||||
</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
|
||||
<script>
|
||||
window.MathJax = {
|
||||
tex: {
|
||||
inlineMath: [['$', '$']],
|
||||
displayMath: [['$$', '$$']],
|
||||
processEscapes: true
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1><a href="/">{{ .Site.Title }}</a></h1>
|
||||
<div class="header-right">
|
||||
<nav>
|
||||
<a href="/resume.pdf">Resume</a>
|
||||
</nav>
|
||||
<button class="theme-toggle" onclick="toggleTheme()" aria-label="Toggle theme">
|
||||
<span class="theme-icon"></span>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
{{ block "main" . }}{{ end }}
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>© {{ now.Year }} {{ .Site.Title }} | <a href="/index.xml">RSS</a> | <a href="/me.gpg">PGP Key</a> | <a href="/resume.pdf">Resume</a> | <a href="https://github.com/Clortox">Github</a> </p>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
function toggleTheme() {
|
||||
const html = document.documentElement;
|
||||
const currentTheme = html.getAttribute('data-theme');
|
||||
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
||||
html.setAttribute('data-theme', newTheme);
|
||||
localStorage.setItem('theme', newTheme);
|
||||
updateThemeIcon();
|
||||
}
|
||||
|
||||
function updateThemeIcon() {
|
||||
const theme = document.documentElement.getAttribute('data-theme');
|
||||
const icon = document.querySelector('.theme-icon');
|
||||
icon.textContent = theme === 'dark' ? '☀️' : '🌙';
|
||||
}
|
||||
|
||||
updateThemeIcon();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
12
layouts/_default/list.html
Normal file
12
layouts/_default/list.html
Normal file
@@ -0,0 +1,12 @@
|
||||
{{ define "main" }}
|
||||
<h1>{{ .Title }}</h1>
|
||||
|
||||
{{ range .Pages }}
|
||||
<article style="margin-bottom: 25px;">
|
||||
<h3><a href="{{ .Permalink }}">{{ .Title }}</a></h3>
|
||||
<p class="date">{{ .Date.Format "January 2, 2006" }}</p>
|
||||
</article>
|
||||
{{ end }}
|
||||
|
||||
<p><a href="/">← Back to home</a></p>
|
||||
{{ end }}
|
||||
@@ -13,13 +13,11 @@
|
||||
{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>Tyler's Posts</title>
|
||||
<title>{{ if eq .Title .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}{{ end }}</title>
|
||||
<link>{{ .Permalink }}</link>
|
||||
<description>Silly little posts I make for no one in particular</description>
|
||||
<generator>Hugo -- gohugo.io</generator>
|
||||
<language>en</language>{{ with .Site.Author.email }}
|
||||
<managingEditor>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</managingEditor>{{end}}{{ with .Site.Author.email }}
|
||||
<webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }}
|
||||
<description>Recent content {{ if ne .Title .Site.Title }}{{ with .Title }}in {{.}} {{ end }}{{ end }}on {{ .Site.Title }}</description>
|
||||
<generator>Hugo -- gohugo.io</generator>{{ with .Site.LanguageCode }}
|
||||
<language>{{.}}</language>{{end}}{{ with .Site.Copyright }}
|
||||
<copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }}
|
||||
<lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
|
||||
{{- with .OutputFormats.Get "RSS" -}}
|
||||
@@ -30,9 +28,8 @@
|
||||
<title>{{ .Title }}</title>
|
||||
<link>{{ .Permalink }}</link>
|
||||
<pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
|
||||
{{ with .Site.Author.email }}<author>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</author>{{end}}
|
||||
<guid>{{ .Permalink }}</guid>
|
||||
<description>{{ .Content | html -}}</description>
|
||||
<guid>{{ .Permalink }}</guid>
|
||||
<description>{{ .Summary | html }}</description>
|
||||
</item>
|
||||
{{ end }}
|
||||
</channel>
|
||||
|
||||
98
layouts/_default/single.html
Normal file
98
layouts/_default/single.html
Normal file
@@ -0,0 +1,98 @@
|
||||
{{ define "main" }}
|
||||
<article class="post-content">
|
||||
<div class="content-wrapper">
|
||||
<h1>{{ .Title }}</h1>
|
||||
<p class="date">{{ .Date.Format "January 2, 2006" }}</p>
|
||||
|
||||
{{ .Content }}
|
||||
|
||||
<div id="footnotes-section"></div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<p>
|
||||
<a href="/">← Back to home</a>
|
||||
{{ with .OutputFormats.Get "markdown" }}
|
||||
| <a href="{{ .RelPermalink }}" download>Download Markdown</a>
|
||||
{{ end }}
|
||||
</p>
|
||||
|
||||
<script>
|
||||
// On mobile, collect sidenotes and display at the end
|
||||
let sidenotesOrganized = false;
|
||||
|
||||
function positionSidenotes() {
|
||||
if (window.innerWidth < 1200) return;
|
||||
|
||||
const contentWrapper = document.querySelector('.content-wrapper');
|
||||
if (!contentWrapper) return;
|
||||
|
||||
const sidenotes = Array.from(document.querySelectorAll('.sidenote'));
|
||||
const containers = Array.from(document.querySelectorAll('.sidenote-container'));
|
||||
|
||||
// Position each sidenote relative to its reference point
|
||||
containers.forEach((container, index) => {
|
||||
const sidenote = container.querySelector('.sidenote');
|
||||
if (!sidenote) return;
|
||||
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
const wrapperRect = contentWrapper.getBoundingClientRect();
|
||||
|
||||
// Calculate position relative to content-wrapper
|
||||
const topPosition = containerRect.top - wrapperRect.top;
|
||||
sidenote.style.top = topPosition + 'px';
|
||||
});
|
||||
|
||||
// Now check for overlaps and adjust
|
||||
for (let i = 0; i < sidenotes.length - 1; i++) {
|
||||
const current = sidenotes[i];
|
||||
const next = sidenotes[i + 1];
|
||||
|
||||
const currentRect = current.getBoundingClientRect();
|
||||
const nextRect = next.getBoundingClientRect();
|
||||
|
||||
// If next sidenote overlaps with current, push it down
|
||||
if (nextRect.top < currentRect.bottom + 10) {
|
||||
const currentTop = parseFloat(current.style.top) || 0;
|
||||
const currentHeight = currentRect.height;
|
||||
next.style.top = (currentTop + currentHeight + 10) + 'px';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function organizeSidenotes() {
|
||||
const sidenotes = document.querySelectorAll('.sidenote');
|
||||
const footnotesSection = document.getElementById('footnotes-section');
|
||||
|
||||
if (!footnotesSection) return;
|
||||
|
||||
if (window.innerWidth < 1200) {
|
||||
// Mobile: show at bottom
|
||||
if (!sidenotesOrganized) {
|
||||
footnotesSection.innerHTML = '<hr style="margin-top: 40px; border: none; border-top: 1px solid var(--border-color);"><h3>Notes</h3>';
|
||||
|
||||
sidenotes.forEach((sidenote) => {
|
||||
const clone = sidenote.cloneNode(true);
|
||||
clone.style.display = 'block';
|
||||
clone.style.position = 'static';
|
||||
clone.style.width = 'auto';
|
||||
clone.style.marginBottom = '15px';
|
||||
footnotesSection.appendChild(clone);
|
||||
});
|
||||
sidenotesOrganized = true;
|
||||
}
|
||||
} else {
|
||||
// Desktop: clear footnotes section and position sidenotes
|
||||
if (sidenotesOrganized) {
|
||||
footnotesSection.innerHTML = '';
|
||||
sidenotesOrganized = false;
|
||||
}
|
||||
setTimeout(positionSidenotes, 100);
|
||||
}
|
||||
}
|
||||
|
||||
organizeSidenotes();
|
||||
window.addEventListener('resize', organizeSidenotes);
|
||||
window.addEventListener('load', positionSidenotes);
|
||||
</script>
|
||||
{{ end }}
|
||||
7
layouts/_default/single.markdown
Normal file
7
layouts/_default/single.markdown
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
title: "{{ .Title }}"
|
||||
date: {{ .Date.Format "2006-01-02" }}
|
||||
{{ if .Draft }}draft: true{{ end }}
|
||||
---
|
||||
|
||||
{{ .RawContent }}
|
||||
@@ -1,32 +1,17 @@
|
||||
{{ define "main" }}
|
||||
<div class="centered top-fade">
|
||||
<div class="intro-content">
|
||||
<h1 data-value="Hi, I'm Tyler" data-show="intro-tagline" class="typewriter"></h1>
|
||||
<div id="intro-tagline">
|
||||
<p data-value="Software engineer, aspiring polymath"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="homepage-card">
|
||||
<div>
|
||||
<h4 data-show="about-description" data-value="uname -a" class="typewriter card-title">~ $ </h1>
|
||||
<br/>
|
||||
<small>About me</small>
|
||||
</div>
|
||||
<div class="about-grid">
|
||||
<div id="about-description" class="about-description">
|
||||
{{ range .Site.Data.index.About.Content }}
|
||||
<p data-value="{{ .value }}"></p>
|
||||
{{ end }}
|
||||
</div>
|
||||
<div class="mini-terminal">
|
||||
<p data-show="about-interests" data-value="cat interests.txt | less" class="typewriter">~ $ </p>
|
||||
<div id="about-interests" class="terminal-contents">
|
||||
{{ range .Site.Data.index.About.Interests }}
|
||||
<p class="terminal-text" data-value="{{ .value }}"></p>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ .Content }}
|
||||
|
||||
<h2>Recent Posts</h2>
|
||||
|
||||
{{ range first 10 (where .Site.RegularPages "Type" "posts") }}
|
||||
<article style="margin-bottom: 30px;">
|
||||
<h3><a href="{{ .Permalink }}">{{ .Title }}</a></h3>
|
||||
<p class="date">{{ .Date.Format "January 2, 2006" }}</p>
|
||||
{{ if .Summary }}
|
||||
<p>{{ .Summary }}</p>
|
||||
{{ end }}
|
||||
</article>
|
||||
{{ end }}
|
||||
|
||||
<p><a href="/posts/">View all posts →</a></p>
|
||||
{{ end }}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
|
||||
<script>
|
||||
MathJax = {
|
||||
tex: {
|
||||
displayMath: [['\\[', '\\]'], ['$$', '$$']], // block
|
||||
inlineMath: [['\\(', '\\)']] // inline
|
||||
},
|
||||
macros: {
|
||||
vect: ["\\boldsymbol{#1}", 1]
|
||||
},
|
||||
chtml: {
|
||||
scale: 1.2
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.MathJax, .mjx-math {
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
|
||||
7
layouts/shortcodes/sidenote.html
Normal file
7
layouts/shortcodes/sidenote.html
Normal file
@@ -0,0 +1,7 @@
|
||||
{{- $id := .Ordinal -}}
|
||||
<span class="sidenote-container">
|
||||
<a href="#sn-{{ $id }}" id="snref-{{ $id }}" class="sidenote-ref">[{{ $id }}]</a>
|
||||
<span id="sn-{{ $id }}" class="sidenote">
|
||||
<a href="#snref-{{ $id }}" class="sidenote-ref">[{{ $id }}]</a> {{ .Inner | markdownify }}
|
||||
</span>
|
||||
</span>
|
||||
Binary file not shown.
@@ -1,20 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2023 YOUR_NAME_HERE
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,2 +0,0 @@
|
||||
+++
|
||||
+++
|
||||
@@ -1,12 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
{{- partial "head.html" . -}}
|
||||
<body>
|
||||
{{- partial "header.html" . -}}
|
||||
<div id="content">
|
||||
<div class="parallax background" style="overflow-y: none;"></div>
|
||||
{{- block "main" . }}{{- end }}
|
||||
</div>
|
||||
{{- partial "footer.html" . -}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,26 +0,0 @@
|
||||
{{ define "main" }}
|
||||
<div class="page-header">
|
||||
<h1 data-value="Posts" class="typewriter scramble"></h1>
|
||||
<p>Ideas, projects, and other musings.</p>
|
||||
</div>
|
||||
{{ range.Pages }}
|
||||
<article style="margin-left: 10vw; margin-right: 10vw;">
|
||||
<a href="{{ .Permalink }}">
|
||||
{{ if .Params.banner }}
|
||||
<div class="post-list-body-container">
|
||||
{{ else }}
|
||||
<div class="post-list-body-container full-width">
|
||||
{{ end }}
|
||||
<h4 data-value="{{ .Title }}" id="{{ .Title | anchorize }}" class="post-list-title">{{ .Title }}</h4>
|
||||
<small class="post-list-date">{{ .Date.Format "January 2, 2006" }}</small>
|
||||
<p>{{ .Summary }}</p>
|
||||
</div>
|
||||
{{ if .Params.banner }}
|
||||
<div class="post-list-image-container">
|
||||
<img src="{{ .Params.banner }}" alt="{{ .Title }}" class="post-list-image" />
|
||||
</div>
|
||||
{{ end }}
|
||||
</a>
|
||||
</article>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -1,11 +0,0 @@
|
||||
{{ define "main" }}
|
||||
<div class="page-header">
|
||||
<h1 data-value="{{ .Title }}" class="scramble">{{ .Title }}</h1>
|
||||
<small class="post-list-date">{{ .Date.Format "January 2, 2006" }}</small>
|
||||
</div>
|
||||
<article>
|
||||
<div class="post clearfix">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
</article>
|
||||
{{ end }}
|
||||
@@ -1,25 +0,0 @@
|
||||
<footer>
|
||||
<div class="footer-sections">
|
||||
<div class="footer-list" style="width: 85%">
|
||||
<h4 id="contact" class="footer-header" style="margin-bottom: 5px">Contact</h4>
|
||||
|
||||
{{ range .Site.Data.footer.Contact }}
|
||||
<a data-value="{{ .name }}" class="scramble" href="{{ .url }}">{{ .name }}</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
<div class="footer-list" style="width: 85%"></div>
|
||||
<div class="footer-list" style="width: 85%">
|
||||
<h4 class="footer-header" style="margin-bottom: 5px">Services</h4>
|
||||
|
||||
{{ range .Site.Data.footer.Services }}
|
||||
<a data-value="{{ .name }}" class="scramble" href="{{ .url }}">{{ .name }}</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
<p style="margin: 0px;">
|
||||
<small>
|
||||
Copyleft <span style="display: inline-block; transform: rotate(180deg);">©</span> {{ now.Year }}.
|
||||
<a style="font-size: 0.8em;" href="https://github.com/Clortox/tylerperkins.xyz">Free open source software.</a>
|
||||
</small>
|
||||
</p>
|
||||
</footer>
|
||||
@@ -1,37 +0,0 @@
|
||||
<head>
|
||||
<link rel="stylesheet" href="{{ "css/styles.css" | relURL }}">
|
||||
<link rel="stylesheet" href="{{ "css/header.css" | relURL }}">
|
||||
<link rel="stylesheet" href="{{ "css/footer.css" | relURL }}">
|
||||
|
||||
<link rel="stylesheet" href="{{ "css/background.css" | relURL }}">
|
||||
<script src="{{ "js/script.js" | relURL }}"></script>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script async src="https://ackee.clortox.com/tracker.js" data-ackee-server="https://ackee.clortox.com" data-ackee-domain-id="8e41f517-df44-41cb-970d-208872712ed6"></script>
|
||||
{{ range .AlternativeOutputFormats -}}
|
||||
{{ printf `<link rel="%s" type="%s" href="%s" title="%s" />` .Rel .MediaType.Type .Permalink $.Site.Title | safeHTML }}
|
||||
{{ end -}}
|
||||
{{ if .Param "math" }}
|
||||
{{ partialCached "math.html" . }}
|
||||
{{ end }}
|
||||
|
||||
<
|
||||
|
||||
<meta name="description" content="Software Engineer, aspiring polymath"/>
|
||||
|
||||
<meta property="og:title" content="{{ if .Title }}{{ .Title }}{{ else }}Tylers Perkins - Software Engineer{{ end }}" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="{{ .Permalink }}" />
|
||||
<meta property="og:image" content="https://opengraph.b-cdn.net/production/images/d8998659-0129-4808-b73c-67797d3fe7ac.jpg?token=X4ji3MDnTCq9oofsKJXYRPV4nIaynTNQmWu0UpE8vaE&height=460&width=460&expires=33281091161">
|
||||
<meta property="og:description" content="Software Engineer, aspiring polymath" />
|
||||
<meta property="og:site_name" content="Tylers Perkins" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:title" content="{{ if .Title }}{{ .Title }}{{ else }}Tylers Perkins - Software Engineer{{ end }}" />
|
||||
<meta property="twitter:description" content="Software Engineer, aspiring polymath" />
|
||||
<meta name="twitter:image" content="https://opengraph.b-cdn.net/production/images/d8998659-0129-4808-b73c-67797d3fe7ac.jpg?token=X4ji3MDnTCq9oofsKJXYRPV4nIaynTNQmWu0UpE8vaE&height=460&width=460&expires=33281091161">
|
||||
<meta property="twitter:image:alt" content="Tylers Perkins Profile Picture">
|
||||
<meta property="twitter:url" content="{{ .Permalink }}" />
|
||||
|
||||
<title>Tylers Perkins - Software Engineer</title>
|
||||
</head>
|
||||
@@ -1,10 +0,0 @@
|
||||
<header class="navbar sticky">
|
||||
{{ range .Site.Data.header.LeftHandSide }}
|
||||
<a data-value="{{ .name }}" class="scramble header-item" href="{{ .url | relURL }}">{{ .name }}</a>
|
||||
{{ end }}
|
||||
<div class="navbar-right">
|
||||
{{ range .Site.Data.header.RightHandSide }}
|
||||
<a data-value="{{ .name }}" class="scramble header-item" href="{{ .url }}">{{ .name }}</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</header>
|
||||
@@ -1,105 +0,0 @@
|
||||
const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
const typeWriterSpeed = 60;
|
||||
|
||||
window.onload = function() {
|
||||
document.querySelectorAll(".scramble").forEach(item => {
|
||||
item.addEventListener("mouseover", scramble);
|
||||
});
|
||||
|
||||
window.addEventListener('scroll', function() {
|
||||
let parallax = document.querySelector('.parallax')
|
||||
let scrollPosition = window.pageYOffset;
|
||||
|
||||
let newSize = (document.querySelector('#content').offsetHeight * 0.8);
|
||||
if(newSize < window.innerHeight)
|
||||
newSize = window.innerHeight;
|
||||
parallax.style.height = newSize + "px";
|
||||
|
||||
parallax.style.transform = 'translateY(' + scrollPosition * .5 + 'px)';
|
||||
})
|
||||
document.querySelectorAll(".typewriter").forEach(item => {
|
||||
observer.observe(item);
|
||||
});
|
||||
}
|
||||
|
||||
function sleep(milliseconds) {
|
||||
return new Promise(resolve => setTimeout(resolve, milliseconds));
|
||||
}
|
||||
|
||||
function replaceSpacesWithNbsp(string) {
|
||||
if (string == null) return;
|
||||
return string.replace(/ /g, "\xa0");
|
||||
}
|
||||
|
||||
function scramble(event){
|
||||
scrambleLetters(event.target);
|
||||
}
|
||||
|
||||
function scrambleLetters(element) {
|
||||
let iterations = 0;
|
||||
const originalValue = element.dataset.value;
|
||||
|
||||
let interval = setInterval(() => {
|
||||
element.innerText = element.innerText.split("")
|
||||
.map((letter, index) => {
|
||||
if(index < iterations){
|
||||
return originalValue[index];
|
||||
}
|
||||
return letters[Math.floor(Math.random() * letters.length)];
|
||||
}).join("")
|
||||
|
||||
iterations += 1 / 3;
|
||||
|
||||
if(iterations > originalValue.length){
|
||||
clearInterval(interval);
|
||||
element.innerText = originalValue;
|
||||
}
|
||||
}, 30);
|
||||
}
|
||||
|
||||
async function typeWriter(element) {
|
||||
let typeWriterText = element.getAttribute("data-value");
|
||||
let output = element.innerText;
|
||||
|
||||
// write the main text
|
||||
for(let i = 0; i < typeWriterText.length; ++i){
|
||||
output += typeWriterText.charAt(i);
|
||||
// sleep for typeWriterSpeed milliseconds
|
||||
await sleep(typeWriterSpeed);
|
||||
element.innerText = output;
|
||||
}
|
||||
|
||||
//try and do the paragraphs associated
|
||||
let hiddenElementId = element.getAttribute("data-show");
|
||||
|
||||
if(hiddenElementId == null) return;
|
||||
|
||||
let hiddenElement = document.getElementById(hiddenElementId);
|
||||
let hiddenElementSpeed = 5;
|
||||
|
||||
let paragraphs = hiddenElement.querySelectorAll("p");
|
||||
for (let p of paragraphs){
|
||||
let hiddenElementText = p.getAttribute("data-value");
|
||||
//hiddenElementText = replaceSpacesWithNbsp(hiddenElementText);
|
||||
p.innerHtml = "";
|
||||
output = "";
|
||||
|
||||
for (let i = 0; i < hiddenElementText.length; ++i){
|
||||
output += hiddenElementText.charAt(i);
|
||||
await sleep(hiddenElementSpeed);
|
||||
p.innerText = output;
|
||||
}
|
||||
|
||||
await sleep(50);
|
||||
}
|
||||
}
|
||||
|
||||
let observer = new IntersectionObserver(function(entries) {
|
||||
for (let entry of entries){
|
||||
// If the element is visible, start the animation
|
||||
if(entry.isIntersecting) {
|
||||
typeWriter(entry.target);
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
}
|
||||
}, { threshold: 0.7 });
|
||||
@@ -1,21 +0,0 @@
|
||||
# theme.toml template for a Hugo theme
|
||||
# See https://github.com/gohugoio/hugoThemes#themetoml for an example
|
||||
|
||||
name = "Clortox"
|
||||
license = "MIT"
|
||||
licenselink = "https://github.com/yourname/yourtheme/blob/master/LICENSE"
|
||||
description = ""
|
||||
homepage = "http://example.com/"
|
||||
tags = []
|
||||
features = []
|
||||
min_version = "0.41.0"
|
||||
|
||||
[author]
|
||||
name = ""
|
||||
homepage = ""
|
||||
|
||||
# If porting an existing theme
|
||||
[original]
|
||||
name = ""
|
||||
homepage = ""
|
||||
repo = ""
|
||||
Reference in New Issue
Block a user