mirror of
https://github.com/jbowdre/runtimeterror.git
synced 2024-11-25 08:22:19 +00:00
Merge branch 'main' into drafts
This commit is contained in:
commit
e785e45cc0
7 changed files with 245 additions and 15 deletions
195
assets/js/typo.js
Normal file
195
assets/js/typo.js
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Typo, a more natural web typing thing
|
||||||
|
|
||||||
|
https://neatnik.net/typo
|
||||||
|
https://github.com/neatnik/typo
|
||||||
|
|
||||||
|
Copyright (c) 2021 Neatnik LLC
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
function num_between(min, max) {
|
||||||
|
return Math.floor(Math.random() * (max- min + 1) + min);
|
||||||
|
}
|
||||||
|
|
||||||
|
function chance(val) {
|
||||||
|
if(num_between(0, 100) < val) return true;
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sleep(ms) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
var typos = {
|
||||||
|
q:['w','a'],
|
||||||
|
w:['q','r','s'],
|
||||||
|
e:['w','d','r'],
|
||||||
|
r:['e','f','t'],
|
||||||
|
t:['r','g','y'],
|
||||||
|
y:['t','h','u'],
|
||||||
|
u:['y','j','i'],
|
||||||
|
i:['u','k','o'],
|
||||||
|
o:['i','l','p'],
|
||||||
|
p:['o',';','['],
|
||||||
|
a:['q','s','z'],
|
||||||
|
s:['w','a','x','d'],
|
||||||
|
d:['e','s','c','f'],
|
||||||
|
f:['r','d','v','g'],
|
||||||
|
g:['t','f','b','h'],
|
||||||
|
h:['y','g','n','j'],
|
||||||
|
j:['u','h','m','k'],
|
||||||
|
k:['i','j',',','l'],
|
||||||
|
l:['o','k','.',';'],
|
||||||
|
z:['a','x'],
|
||||||
|
x:['z','s','c'],
|
||||||
|
c:['x','d','v'],
|
||||||
|
v:['c','f','b'],
|
||||||
|
b:['v','g','n'],
|
||||||
|
n:['b','h','m'],
|
||||||
|
m:['n','j',','],
|
||||||
|
}
|
||||||
|
|
||||||
|
async function typo(element, text) {
|
||||||
|
var buffer = '';
|
||||||
|
var typo_active = false;
|
||||||
|
var tag_active = false;
|
||||||
|
var typing_typos = (element.dataset.typoChance) ? element.dataset.typoChance : 10;
|
||||||
|
var typing_speed = (element.dataset.typingDelay) ? element.dataset.typingDelay : 50;
|
||||||
|
var typing_jitter = (element.dataset.typingJitter) ? element.dataset.typingJitter : 15;
|
||||||
|
|
||||||
|
for (var i = 0; i < text.length; i++) {
|
||||||
|
|
||||||
|
// Get the letter that we’re supposed to type
|
||||||
|
letter = text.charAt(i);
|
||||||
|
|
||||||
|
// TODO: actual support for html or markup or whatever
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Handle elements/markup
|
||||||
|
if(letter == '<' && (
|
||||||
|
text.charAt(i+1) == 's' ||
|
||||||
|
text.charAt(i+1) == 'p' ||
|
||||||
|
text.charAt(i+1) == 'a' ||
|
||||||
|
text.charAt(i+1) == '/' ||
|
||||||
|
text.charAt(i+1) == 'i')
|
||||||
|
) {
|
||||||
|
tag_active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(tag_active) {
|
||||||
|
|
||||||
|
buffer = buffer + letter;
|
||||||
|
element.innerHTML = buffer;
|
||||||
|
|
||||||
|
if(letter == '>' && (
|
||||||
|
text.charAt(i-1) == 'n' ||
|
||||||
|
text.charAt(i-1) == 'a' ||
|
||||||
|
text.charAt(i-1) == 'p' ||
|
||||||
|
text.charAt(i+1) == '"' ||
|
||||||
|
text.charAt(i+1) == '/')
|
||||||
|
) {
|
||||||
|
tag_active = false;
|
||||||
|
await sleep(typing_speed);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Trigger a typo
|
||||||
|
if(chance(typing_typos) && typo_active === false && i > 1) {
|
||||||
|
|
||||||
|
if(typeof typos[letter] !== 'undefined') {
|
||||||
|
|
||||||
|
// Swap the letter with a random typo
|
||||||
|
typo = typos[letter][Math.floor(Math.random() * typos[letter].length)];
|
||||||
|
|
||||||
|
// Append the letter to the buffer
|
||||||
|
buffer = buffer + typo;
|
||||||
|
|
||||||
|
// Write the buffer
|
||||||
|
element.innerHTML = buffer;
|
||||||
|
|
||||||
|
typo_active = true;
|
||||||
|
var seed = num_between(2,5);
|
||||||
|
realization_delay = seed;
|
||||||
|
realization_delay_counter = seed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the letter to the buffer
|
||||||
|
buffer = buffer + letter;
|
||||||
|
|
||||||
|
// Write the buffer
|
||||||
|
element.innerHTML = buffer;
|
||||||
|
|
||||||
|
// Typical typing speed
|
||||||
|
var speed_lower = parseFloat(typing_speed) - parseInt(typing_jitter);
|
||||||
|
var speed_upper = parseFloat(typing_speed) + parseInt(typing_jitter);
|
||||||
|
|
||||||
|
delay = num_between(speed_lower,speed_upper);
|
||||||
|
|
||||||
|
// Chance of longer delay though
|
||||||
|
if(chance(5)) delay = num_between(100, 200);
|
||||||
|
await sleep(delay);
|
||||||
|
|
||||||
|
if(typo_active) {
|
||||||
|
|
||||||
|
realization_delay_counter--;
|
||||||
|
|
||||||
|
if(realization_delay_counter == 0) {
|
||||||
|
|
||||||
|
for (var k = 0; k < seed+1; k++) {
|
||||||
|
|
||||||
|
// Pause at realization of typo
|
||||||
|
await sleep(typing_jitter);
|
||||||
|
|
||||||
|
// Rewind the buffer!
|
||||||
|
buffer = buffer.substring(0, buffer.length - 1);
|
||||||
|
|
||||||
|
// Write rewound buffer
|
||||||
|
element.innerHTML = buffer;
|
||||||
|
|
||||||
|
// Brief pause before continuing
|
||||||
|
await sleep(30);
|
||||||
|
}
|
||||||
|
|
||||||
|
typo_active = false;
|
||||||
|
|
||||||
|
// Add the letters back
|
||||||
|
i = i - seed;
|
||||||
|
await sleep(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whatever you do here will happen when the typing is finished
|
||||||
|
//do_something();
|
||||||
|
|
||||||
|
return new Promise(resolve => setTimeout(resolve, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
var element = document.getElementById('tagline');
|
||||||
|
var text = element.innerHTML;
|
||||||
|
typo(element, text);
|
||||||
|
});
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
title: "/changelog"
|
title: "/changelog"
|
||||||
date: "2024-05-26T21:19:08Z"
|
date: "2024-05-26T21:19:08Z"
|
||||||
lastmod: "2024-06-07T22:41:44Z"
|
lastmod: "2024-06-13T16:20:54Z"
|
||||||
description: "Maybe I should keep a log of all my site-related tinkering?"
|
description: "Maybe I should keep a log of all my site-related tinkering?"
|
||||||
featured: false
|
featured: false
|
||||||
toc: false
|
toc: false
|
||||||
|
@ -10,6 +10,9 @@ categories: slashes
|
||||||
---
|
---
|
||||||
*High-level list of config/layout changes to the site.*
|
*High-level list of config/layout changes to the site.*
|
||||||
|
|
||||||
|
**2024-06-13:**
|
||||||
|
- Add [Typo](https://neatnik.net/typo/) and a blinking cursor to the random taglines in the sidebar
|
||||||
|
|
||||||
**2024-06-06:**
|
**2024-06-06:**
|
||||||
- Migrate hosting from [Neocities to Bunny CDN](/further-down-the-bunny-hole/)
|
- Migrate hosting from [Neocities to Bunny CDN](/further-down-the-bunny-hole/)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
title: "/colophon"
|
title: "/colophon"
|
||||||
date: "2024-05-26T22:30:58Z"
|
date: "2024-05-26T22:30:58Z"
|
||||||
lastmod: "2024-06-06"
|
lastmod: "2024-06-13T22:06:26Z"
|
||||||
description: "There's a lot that goes into this site. Let me tell you how it works."
|
description: "There's a lot that goes into this site. Let me tell you how it works."
|
||||||
featured: false
|
featured: false
|
||||||
toc: true
|
toc: true
|
||||||
|
@ -17,7 +17,7 @@ categories: slashes
|
||||||
- provides site search with [lunr](https://lunrjs.com/) based on an implementation detailed by [Victoria Drake](https://victoria.dev/blog/add-search-to-hugo-static-sites-with-lunr/).
|
- provides site search with [lunr](https://lunrjs.com/) based on an implementation detailed by [Victoria Drake](https://victoria.dev/blog/add-search-to-hugo-static-sites-with-lunr/).
|
||||||
- leverages [tinylytics](https://tinylytics.app/) for privacy-friendly analytics and cute kudos buttons.
|
- leverages [tinylytics](https://tinylytics.app/) for privacy-friendly analytics and cute kudos buttons.
|
||||||
- resolves via [Bunny DNS](https://bunny.net/dns/).
|
- resolves via [Bunny DNS](https://bunny.net/dns/).
|
||||||
- is published to / hosted by [Bunny CDN](https://bunny.net/cdn/) with a GitHub Actions workflow.
|
- is published to / hosted on [Bunny Storage](https://bunny.net/storage/) and [Bunny CDN](https://bunny.net/cdn/) with a [GitHub Actions workflow](//further-down-the-bunny-hole/)
|
||||||
- has a [Gemini](https://geminiprotocol.net) mirror at `gemini://gmi.runtimeterror.dev`. This is generated from a [Hugo gemtext post layout](https://github.com/jbowdre/runtimeterror/blob/main/layouts/_default/single.gmi), deployed to a [Vultr](https://www.vultr.com/) VPS through a GitHub Actions workflow, and served with [Agate](https://github.com/mbrubeck/agate).
|
- has a [Gemini](https://geminiprotocol.net) mirror at `gemini://gmi.runtimeterror.dev`. This is generated from a [Hugo gemtext post layout](https://github.com/jbowdre/runtimeterror/blob/main/layouts/_default/single.gmi), deployed to a [Vultr](https://www.vultr.com/) VPS through a GitHub Actions workflow, and served with [Agate](https://github.com/mbrubeck/agate).
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
title: "Blocking AI Crawlers"
|
title: "Blocking AI Crawlers"
|
||||||
date: 2024-04-12
|
date: 2024-04-12
|
||||||
lastmod: "2024-04-14T02:21:57Z"
|
lastmod: "2024-06-13T20:51:54Z"
|
||||||
description: "Using Hugo to politely ask AI bots to not steal my content - and then configuring Cloudflare's WAF to actively block them, just to be sure."
|
description: "Using Hugo to politely ask AI bots to not steal my content - and then configuring Cloudflare's WAF to actively block them, just to be sure."
|
||||||
featured: false
|
featured: false
|
||||||
toc: true
|
toc: true
|
||||||
|
@ -24,7 +24,7 @@ robots = [
|
||||||
"AdsBot-Google",
|
"AdsBot-Google",
|
||||||
"Amazonbot",
|
"Amazonbot",
|
||||||
"anthropic-ai",
|
"anthropic-ai",
|
||||||
"Applebot",
|
"Applebot-Extended",
|
||||||
"AwarioRssBot",
|
"AwarioRssBot",
|
||||||
"AwarioSmartBot",
|
"AwarioSmartBot",
|
||||||
"Bytespider",
|
"Bytespider",
|
||||||
|
@ -47,9 +47,6 @@ robots = [
|
||||||
"PerplexityBot",
|
"PerplexityBot",
|
||||||
"YouBot"
|
"YouBot"
|
||||||
]
|
]
|
||||||
|
|
||||||
[author]
|
|
||||||
name = "John Bowdre"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
I then created a new template in `layouts/robots.txt`:
|
I then created a new template in `layouts/robots.txt`:
|
||||||
|
@ -57,9 +54,14 @@ I then created a new template in `layouts/robots.txt`:
|
||||||
```text
|
```text
|
||||||
Sitemap: {{ .Site.BaseURL }}/sitemap.xml
|
Sitemap: {{ .Site.BaseURL }}/sitemap.xml
|
||||||
|
|
||||||
|
# hello robots [^_^]
|
||||||
|
# let's be friends <3
|
||||||
|
|
||||||
User-agent: *
|
User-agent: *
|
||||||
Disallow:
|
Disallow:
|
||||||
{{ range .Site.Params.robots }}
|
|
||||||
|
# except for these bots which are not friends:
|
||||||
|
{{ range .Site.Params.bad_robots }}
|
||||||
User-agent: {{ . }}
|
User-agent: {{ . }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
Disallow: /
|
Disallow: /
|
||||||
|
@ -74,15 +76,20 @@ enableRobotsTXT = true
|
||||||
Now Hugo will generate the following `robots.txt` file for me:
|
Now Hugo will generate the following `robots.txt` file for me:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Sitemap: https://runtimeterror.dev//sitemap.xml
|
Sitemap: https://runtimeterror.dev/sitemap.xml
|
||||||
|
|
||||||
|
# hello robots [^_^]
|
||||||
|
# let's be friends <3
|
||||||
|
|
||||||
User-agent: *
|
User-agent: *
|
||||||
Disallow:
|
Disallow:
|
||||||
|
|
||||||
|
# except for these bots which are not friends:
|
||||||
|
|
||||||
User-agent: AdsBot-Google
|
User-agent: AdsBot-Google
|
||||||
User-agent: Amazonbot
|
User-agent: Amazonbot
|
||||||
User-agent: anthropic-ai
|
User-agent: anthropic-ai
|
||||||
User-agent: Applebot
|
User-agent: Applebot-Extended
|
||||||
User-agent: AwarioRssBot
|
User-agent: AwarioRssBot
|
||||||
User-agent: AwarioSmartBot
|
User-agent: AwarioSmartBot
|
||||||
User-agent: Bytespider
|
User-agent: Bytespider
|
||||||
|
@ -129,7 +136,7 @@ So I added a [WAF Custom Rule](https://developers.cloudflare.com/waf/custom-rule
|
||||||
Here's the expression I'm using:
|
Here's the expression I'm using:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
(http.user_agent contains "AdsBot-Google") or (http.user_agent contains "Amazonbot") or (http.user_agent contains "anthropic-ai") or (http.user_agent contains "Applebot") or (http.user_agent contains "AwarioRssBot") or (http.user_agent contains "AwarioSmartBot") or (http.user_agent contains "Bytespider") or (http.user_agent contains "CCBot") or (http.user_agent contains "ChatGPT-User") or (http.user_agent contains "ClaudeBot") or (http.user_agent contains "Claude-Web") or (http.user_agent contains "cohere-ai") or (http.user_agent contains "DataForSeoBot") or (http.user_agent contains "FacebookBot") or (http.user_agent contains "Google-Extended") or (http.user_agent contains "GoogleOther") or (http.user_agent contains "GPTBot") or (http.user_agent contains "ImagesiftBot") or (http.user_agent contains "magpie-crawler") or (http.user_agent contains "Meltwater") or (http.user_agent contains "omgili") or (http.user_agent contains "omgilibot") or (http.user_agent contains "peer39_crawler") or (http.user_agent contains "peer39_crawler/1.0") or (http.user_agent contains "PerplexityBot") or (http.user_agent contains "Seekr") or (http.user_agent contains "YouBot")
|
(http.user_agent contains "AdsBot-Google") or (http.user_agent contains "Amazonbot") or (http.user_agent contains "anthropic-ai") or (http.user_agent contains "Applebot-Extended") or (http.user_agent contains "AwarioRssBot") or (http.user_agent contains "AwarioSmartBot") or (http.user_agent contains "Bytespider") or (http.user_agent contains "CCBot") or (http.user_agent contains "ChatGPT-User") or (http.user_agent contains "ClaudeBot") or (http.user_agent contains "Claude-Web") or (http.user_agent contains "cohere-ai") or (http.user_agent contains "DataForSeoBot") or (http.user_agent contains "FacebookBot") or (http.user_agent contains "Google-Extended") or (http.user_agent contains "GoogleOther") or (http.user_agent contains "GPTBot") or (http.user_agent contains "ImagesiftBot") or (http.user_agent contains "magpie-crawler") or (http.user_agent contains "Meltwater") or (http.user_agent contains "omgili") or (http.user_agent contains "omgilibot") or (http.user_agent contains "peer39_crawler") or (http.user_agent contains "peer39_crawler/1.0") or (http.user_agent contains "PerplexityBot") or (http.user_agent contains "Seekr") or (http.user_agent contains "YouBot")
|
||||||
```
|
```
|
||||||
|
|
||||||
![Creating a custom WAF rule in Cloudflare's web UI](cloudflare-waf-rule.png)
|
![Creating a custom WAF rule in Cloudflare's web UI](cloudflare-waf-rule.png)
|
||||||
|
|
|
@ -49,3 +49,7 @@
|
||||||
{{ $copyCss := resources.Get "css/code-copy-button.css" | minify }}
|
{{ $copyCss := resources.Get "css/code-copy-button.css" | minify }}
|
||||||
<link href="{{ $copyCss.RelPermalink }}" rel="stylesheet">
|
<link href="{{ $copyCss.RelPermalink }}" rel="stylesheet">
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- typo text animation -->
|
||||||
|
{{ $jsTypo := resources.Get "js/typo.js" | minify }}
|
||||||
|
<script src="{{ $jsTypo.RelPermalink }}"></script>
|
|
@ -4,9 +4,11 @@
|
||||||
<script language="Javascript" type="text/javascript">
|
<script language="Javascript" type="text/javascript">
|
||||||
var taglines = JSON.parse(document.getElementsByTagName("meta")["taglines"].content);
|
var taglines = JSON.parse(document.getElementsByTagName("meta")["taglines"].content);
|
||||||
function getTagline() {
|
function getTagline() {
|
||||||
var randIndex = Math.floor(Math.random() * taglines.length);
|
const randIndex = Math.floor(Math.random() * taglines.length);
|
||||||
document.getElementById("tagline").innerHTML = taglines[randIndex];
|
const element = document.getElementById("tagline");
|
||||||
|
element.innerHTML = taglines[randIndex];
|
||||||
|
typo(element, element.innerHTML);
|
||||||
}
|
}
|
||||||
window.addEventListener("pageshow", getTagline);
|
window.addEventListener("pageshow", getTagline);
|
||||||
</script>
|
</script>
|
||||||
<div><i id="tagline"></i></div>
|
<div style="height: 3.5rem;"><i id="tagline" data-typo-chance="2" data-typing-delay="40" data-typing-jitter="20"></i></div>
|
||||||
|
|
|
@ -402,3 +402,22 @@ p:has(+ ol),
|
||||||
p:has(+ ul) {
|
p:has(+ ul) {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* tagline cursor animation */
|
||||||
|
#tagline::after {
|
||||||
|
content: "";
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 1.2rem;
|
||||||
|
background: var(--muted);
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 0.2rem;
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
animation: cursor-blink 1.5s steps(2) infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes cursor-blink {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue