add initial site content, basic templates
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: "{{ replace .Name "-" " " | title }}"
|
||||
date: {{ .Date }}
|
||||
draft: true
|
||||
---
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
baseURL: http://yequari.com/
|
||||
languageCode: en-us
|
||||
title: yequari.com
|
||||
theme: genesis
|
|
@ -0,0 +1,88 @@
|
|||
---
|
||||
title: "Links"
|
||||
date: 2023-03-22T20:37:43-07:00
|
||||
draft: true
|
||||
---
|
||||
|
||||
<div id="links-content">
|
||||
<div id="links-top">
|
||||
<p>
|
||||
This is a collection of pages and websites I have stumbled upon that I think are cool. Some are members of the greater Yesterweb community and others are pages I want to revisit, I think others will find useful, or are just fun.
|
||||
</p>
|
||||
</div>
|
||||
<div id="links-main">
|
||||
<div id="links-left" class="links-column">
|
||||
<ul class="links">
|
||||
<h4>Computing</h4>
|
||||
<li>
|
||||
<a class="hover-links" target="_blank" href="https://sbtcvm.blogspot.com/?m=1" data-hover="A virtual machine simulating a base 3 computer. Alternatives to the traditional models of computing are of great interest to me and I want to learn more about programming in base 3.">Simple Balanced Ternary Computer Virtual Machine</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="hover-links" target="_blank" href="https://sharats.me/posts/shell-script-best-practices/" data-hover="A list of tips when writing shell scripts. Especially useful for me since I'm not super familiar with bash but use it on a daily basis.">Shell Script Best Practices</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="hover-links" target="_blank" href="http://www.catb.org/esr/structure-packing/" data-hover="An interesting look into how C structs work and how to squeeze some extra optimization out of them.">The Lost Art of Structure Packing</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="hover-links" target="_blank" href="https://webmentions.neocities.org" data-hover="An excellent tutorial on how to set up webmentions on your site, specifically tailored to neocities users.">how do I webmentions</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="hover-links" target="_blank" href="https://sebhastian.com/html-hover-text/" data-hover="A tutorial explaining hover text without using JavaScript, which I used to set up this page.">How to create hover text using HTML and CSS</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="links-center" class="links-column">
|
||||
<ul class="links">
|
||||
<h4>Amusement</h4>
|
||||
<li>
|
||||
<a class="hover-links" target="_blank" href="http://www.norvig.com/Gettysburg/index.htm" data-hover="A modern interpretation of the Gettysburg Address.">The Gettysburg Powerpoint Presentation</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="hover-links" target="_blank" href="https://dev.to/miguelmota/how-unix-programmers-at-restaurants-search-menus--46ad" data-hover="A real-world application of UNIX skills.">How Unix programmers at restaurants search menus for their favorite plate</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="hover-links" target="blank" href="https://xwellingtonx.github.io/advanced-pet/" data-hover="A web simulation of the old Megaman NT Warrior Advanced PET toy from the mid-2000s">Advanced PET</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="links">
|
||||
<h4>Interesting Articles</h4>
|
||||
<li>
|
||||
<a class="hover-links" target="_blank" href="https://www.wired.com/2016/07/forget-comcast-heres-the-diy-approach-to-internet-access/" data-hover="A Wired article about a Spanish engineer who built up a distributed wifi network to provide Internet to his town because telecom companies wouldn't.">Forget Comcast. Here’s The DIY Approach to Internet Access</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="hover-links" target="_blank" href="https://socket3.wordpress.com/2018/02/03/designing-windows-95s-user-interface/" data-hover="A retrospective from someone who worked on the Windows 95 user interface detailing the design process and iteration that went into creating an interface that was accessible to both first-time computer users and existing power users.">Designing Windows 95's User Interface</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="links-right" class="links-column">
|
||||
<ul class="links">
|
||||
<h4>Food for Thought</h4>
|
||||
<li>
|
||||
<a class="hover-links" target="_blank" href="https://yesterweb.org" data-hover="The Yesterweb is a movement promoting smaller, more personalized communities on the web. Many members maintain their own personal websites, often on Neocities.">Yesterweb</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="hover-links" target="_blank" href="https://permacomputing.net" data-hover="Permacomputing focuses on sustainable computing, inspired by permaculture, aiming to utilize existing compute resources rather than new ones. The wiki is very incomplete, but is something to keep an eye on as it grows. ">Permacomputing Wiki</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="hover-links" target="_blank" href="https://permies.com/w/better-world" data-hover="On the topic of permaculture, Building a Better World in your Backyard is a book describing how you can get started with permaculture and living a sustainable lifestyle. The website has lots of good resources and is often referenced by the book. You can also buy the book in bulk for cheap to distribute amongst your local community.">Building a Better World in your Backyard</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="hover-links" target="_blank" href="https://ajroach42.com/the-small-things-manifesto/" data-hover="A manifesto written by Andrew Roach proposing that we should try to do things on a human scale again, instead of trying to design everything to scale infinitely.">The small things Manifesto</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div id="links-bottom">
|
||||
|
||||
<h4>Yesterweb Friends</h4>
|
||||
<a href="https://xandra.cc"><img src="/static/images/buttons/xandracc.png"></a>
|
||||
<a href="https://www.ifelse95.xyz"><img src="/static/images/buttons/ifelse95xyz.gif"></a>
|
||||
<a href="https://seapunk.xyz"><img src="/static/images/buttons/seapunkxyz.gif"></a>
|
||||
<a href="https://acebit.neocities.org/"><img src="/static/images/buttons/acebit.png"></a>
|
||||
<a href="https://www.cyberdragon.digital/"><img src="/static/images/buttons/cyberdragon.png"></a>
|
||||
<a href="https://mothcore.neocities.org/"><img src="/static/images/buttons/mothcore2.gif"></a>
|
||||
<a href="https://supveronica.neocities.org/"><img src="/static/images/buttons/supveronica.gif"></a>
|
||||
<a href="https://sinclair-speccy.github.io/The-Mainframe/"><img src="/static/images/buttons/themainframe.png"></a>
|
||||
<a href="queenofarms.neocities.org"><img src="/static/images/buttons/queenofarms.gif"></a>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,82 @@
|
|||
---
|
||||
title: Dockerized ZNC
|
||||
date: "2022-11-07T14:18:30-07:00"
|
||||
categories:
|
||||
- tech
|
||||
---
|
||||
|
||||
# Setting Up ZNC with Docker
|
||||
|
||||
ZNC is an IRC bouncer, which is a program that acts as a middleman between your IRC client and any servers you connect to. This provides a number of benefits, the most notable of which is chat history while your client is closed. This guide is how I set up
|
||||
|
||||
I have a Raspberry Pi connected to my network that I use as a DNS server with [Pi-hole](https://pi-hole.net) and [nginx-proxy](https://hub.docker.com/r/nginxproxy/nginx-proxy), as well as a host for several web-based services. Each service is a Docker container, which I manage through a single Docker Compose file. To install ZNC, I simply add a container to the Compose file.
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
|
||||
# https://github.com/pi-hole/docker-pi-hole/blob/master/README.md
|
||||
|
||||
services:
|
||||
nginx-proxy:
|
||||
image: nginxproxy/nginx-proxy
|
||||
ports:
|
||||
- '80:80'
|
||||
environment:
|
||||
DEFAULT_HOST: pihole.home.yequari.com
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/tmp/docker.sock'
|
||||
restart: always
|
||||
|
||||
pihole:
|
||||
image: pihole/pihole:latest
|
||||
ports:
|
||||
- '53:53/tcp'
|
||||
- '53:53/udp'
|
||||
- '8053:80/tcp'
|
||||
volumes:
|
||||
- './etc-pihole:/etc/pihole'
|
||||
- './etc-dnsmasq.d:/etc/dnsmasq.d'
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
environment:
|
||||
ServerIP: 192.168.1.130
|
||||
PROXY_LOCATION: pihole
|
||||
VIRTUAL_HOST: pihole.home.yequari.com
|
||||
VIRTUAL_PORT: 80
|
||||
extra_hosts:
|
||||
# Resolve to nothing domains (terminate connection)
|
||||
# - 'nw2master.bioware.com nwn2.master.gamespy.com:0.0.0.0'
|
||||
# LAN hostnames for other docker containers using nginx-proxy
|
||||
- 'home.yequari.com:192.168.1.130'
|
||||
- 'pihole pihole.home.yequari.com:192.168.1.130'
|
||||
- 'jellyfin jellyfin.home.yequari.com:192.168.1.130'
|
||||
- 'freshrss freshrss.home.yequari.com:192.168.1.130'
|
||||
- 'znc znc.home.yequari.com:192.168.1.130'
|
||||
restart: always
|
||||
|
||||
## entries for other services omitted
|
||||
|
||||
znc:
|
||||
image: lscr.io/linuxserver/znc:latest
|
||||
container_name: znc
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=America/Phoenix
|
||||
- PROXY_LOCATION=znc
|
||||
- VIRTUAL_HOST=znc.home.yequari.com
|
||||
- VIRTUAL_PORT=6501
|
||||
volumes:
|
||||
- /mnt/hdisk1/znc:/config
|
||||
ports:
|
||||
- 6501:6501
|
||||
- 6697:6697
|
||||
restart: unless-stopped
|
||||
|
||||
```
|
||||
|
||||
The `VIRTUAL_HOST` domain name will redirect only to the web interface on port 6501. I also reserved port 6697 for IRC itself. While it is possible to reverse proxy the IRC port to use the domain name as well, it was more trouble that it was worth, so I just used the Raspberry Pi's local IP address to connect my IRC client. Though the official ZNC Docker image should work fine, I used the [Linuxserver image](https://hub.docker.com/r/linuxserver/znc), simply because I used their Jellyfin image previously due to its added support for Raspberry Pi systems.
|
||||
|
||||
Once that was done, I logged into the web interface (default login is admin/admin) to create a new user and connect it to my preferred IRC network. These steps are pretty straightforward and well-documented, so I'll skip over them.
|
||||
|
||||
Now it's finally time to connect the IRC client to the ZNC server. I created a connection as normal, but used the ZNC server's IP address instead of the domain name. Double check port is correct (6697 in my case). Finally, I needed to provide a password of the format `<username>/<network>:<password>`, using the information I provided ZNC when setting up my user. For example `yequari/OFTC:supersecretpassword`. Note that `<network>` refers to the name you give the network in ZNC, not the URL. I was then able to connect and enjoy all the benefits of an IRC bouncer.
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
title: gitguide
|
||||
date: "2022-04-26T13:48:03-07:00"
|
||||
categories:
|
||||
- tech
|
||||
draft: true
|
||||
---
|
||||
Git is a version control software, a type of software the keeps track of versions of files in a directory. It was created by Linus Torvalds (the Linux guy) because he needed a way to easily track changes while maintaining the Linux source code
|
||||
|
||||
## Basic Workflow
|
||||
|
||||
Git tracks changes (i.e. versions) of your files in a repository. A repository is a directory on your computer that contains your files and git's metadata about the changes. To turn a directory on your computer into a repository, navigate to your directory in a terminal and run this command.
|
||||
git init .
|
||||
This has created an empty repo in the directory. The next step is to add some files.
|
||||
- commit, pull, push, remote
|
||||
|
||||
## Branching
|
||||
|
||||
- checkout, merge
|
||||
|
||||
## Best Practices
|
||||
|
||||
TODO: how to protect sensitive information (passwords, API keys, etc)
|
||||
|
||||
TODO: you've committed some sensitive information, now what?
|
||||
- undo commit
|
||||
- if you've pushed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: "I Bought a Thinkpad"
|
||||
date: 2023-03-22T18:09:19-07:00
|
||||
---
|
||||
Specifically, I got a ThinkPad T450s from 2015 for about $40. It didn’t come with storage, a power adapter, or the external battery, which cost me about an extra $100 to order. The internal battery is there but I can’t really test it until my power adapter comes in (I forgot to order it at the same time as the laptop whoops). There are also some keycaps missing. I can’t test if the switches are good yet but hopefully it turns out to be an easy fix. When I removed the back panel, I found that the plastic towards the front that the screws go into was broken, as if someone had just ripped the thing open with the screws still in there. One screw’s plastic is completely gone, but the other two are there, loosely hanging around, which is annoying, but some super glue should fix that right up. Assuming the laptop works once all my parts come in, I can’t be too mad over a $140 laptop.
|
|
@ -0,0 +1,83 @@
|
|||
---
|
||||
title: League on Linux
|
||||
date: "2022-11-06T14:18:30-07:00"
|
||||
categories:
|
||||
- tech
|
||||
---
|
||||
|
||||
# League of Legends on Linux
|
||||
|
||||
*LoLoL for short*
|
||||
|
||||
These are my notes on how I got League of Legends running on my Linux system. It runs almost flawlessly, the only issues I have are with the client, but even on Windows the client is garbage. The game itself runs even better on Linux than it does Windows.
|
||||
|
||||
### System Info
|
||||
OS: Kubuntu 22.04 LTS x86_64
|
||||
CPU: AMD Ryzen 5 5600X (12) @ 3.700GHz
|
||||
GPU: NVIDIA GeForce GTX 970
|
||||
RAM: 8GB
|
||||
|
||||
### Credits
|
||||
|
||||
I learned how to do this from the community over at [r/leagueoflinux](https://old.reddit.com/r/leagueoflinux) so for any questions or issues, check over there. These are posts that were especially helpful to me.
|
||||
|
||||
- [Fixing the Nvidia 495 Black Screen Crash Problem](https://old.reddit.com/r/leagueoflinux/comments/skyg70/fixing_the_nvidia_495_black_screen_crash_problem/)
|
||||
- [Decreased Client Lag No More Low-Spec Mode](https://www.reddit.com/r/leagueoflinux/comments/qyjv45/decreased_client_lag_no_more_low_spec_mode/)
|
||||
- [r/leagueoflinux Wiki](https://old.reddit.com/r/leagueoflinux/wiki/index)
|
||||
|
||||
## 1. Install Lutris
|
||||
|
||||
### a. Install Lutris Client
|
||||
|
||||
```
|
||||
sudo add-apt-repository ppa:lutris-team/lutris && sudo apt update && sudo apt install lutris
|
||||
```
|
||||
|
||||
### b. Complete Prerequisites for League
|
||||
|
||||
Enable 32-bit architecture
|
||||
```
|
||||
sudo dpkg --add-architecture i386
|
||||
```
|
||||
|
||||
Install the latest Nvidia and Vulkan drivers from the proprietary drivers PPA
|
||||
|
||||
```
|
||||
sudo add-apt-repository ppa:graphics-drivers/ppa && sudo apt update && sudo apt install -y nvidia-driver-510 libvulkan1 libvulkan1:i386
|
||||
```
|
||||
|
||||
Install Wine Dependencies
|
||||
|
||||
```
|
||||
sudo apt update && sudo apt install -y wine64 wine32 libasound2-plugins:i386 libsdl2-2.0-0:i386 libdbus-1-3:i386 libsqlite3-0:i386
|
||||
```
|
||||
|
||||
### c. Download League of Legends
|
||||
|
||||
https://lutris.net/games/league-of-legends/
|
||||
|
||||
Install standard version.
|
||||
|
||||
Follow the instructions that Lutris presents. There are two downloads: the client and the game. ***IMPORTANT:*** Do NOT enter login details or click play. Simply close the laucher when downloads are complete.
|
||||
|
||||
## 2. Optimizations and Workarounds
|
||||
|
||||
### a. Delete and Disable DXVK cache
|
||||
This prevents the game from getting stuck on a black screen when loading
|
||||
1. Remove all files with `cxvk-cache` in their name from `~/Games/league-of-legends`
|
||||
2. In Lutris, right click League of Legends -> Configure -> System Options -> Environment Variables, remove `DXVK_STATE_CACHE_PATH` and add `DXVK_STATE_CACHE=0`
|
||||
|
||||
### b. Add d3d Overrides
|
||||
This improves client performance
|
||||
1. In Lutris, League of Legends -> Wine configuration -> Libraries, add `d3d10 (native)` and `d3d11 (native)`
|
||||
|
||||
### c. Disable abi.vsyscall
|
||||
```
|
||||
sudo sysctl -w abi.vsyscall=0
|
||||
```
|
||||
|
||||
This command needs to be run every time you boot your machine, or you will get an error when loading the game. There is a way to make this happen automatically, but I rarely fully shutdown my PC so it's not necessary for me.
|
||||
|
||||
### d. Black screen when loading into game
|
||||
|
||||
Sometimes when something goes wrong, the game will open a black screen and refuse to load. I'm not sure the cause of this but the only fix I've found is to restart your system.
|
|
@ -0,0 +1 @@
|
|||
{"Target":"ananke/css/main.min.css","MediaType":"text/css","Data":{}}
|
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 313 KiB |
After Width: | Height: | Size: 899 B |
After Width: | Height: | Size: 1008 B |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 628 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 902 B |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 300 KiB |
After Width: | Height: | Size: 306 KiB |
After Width: | Height: | Size: 330 KiB |
After Width: | Height: | Size: 169 KiB |
|
@ -0,0 +1,20 @@
|
|||
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.
|
|
@ -0,0 +1,2 @@
|
|||
+++
|
||||
+++
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
{{- partial "head.html" . -}}
|
||||
<body>
|
||||
<div id="vent"></div>
|
||||
<div class="column">
|
||||
{{- partial "header.html" . -}}
|
||||
<div id="container">
|
||||
{{- block "main" . }}{{- end }}
|
||||
</div>
|
||||
{{- partial "footer.html" . -}}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,14 @@
|
|||
{{ define "main" }}
|
||||
<div id="sidebar"></div>
|
||||
<div id="content">
|
||||
<h1>{{ .Title }}</h1>
|
||||
{{ range .Pages.ByPublishDate.Reverse }}
|
||||
<p>
|
||||
<h3><a class="title" href="{{ .RelPermalink }}">{{ .Title }}</a></h3>
|
||||
<a class="summary" href="{{ .RelPermalink }}">
|
||||
<p>{{ .Summary }}</p>
|
||||
</a>
|
||||
</p>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
|
@ -0,0 +1,14 @@
|
|||
{{ define "main" }}
|
||||
<div id="content">
|
||||
<article class="h-entry">
|
||||
<h1 class="p-name">{{ .Title }}</h1>
|
||||
<a class="p-author h-card hidden" href="https://yequari.com">yequari</a>
|
||||
<time class="dt-published" datetime={{.PublishDate }}>{{ .PublishDate }}</time>
|
||||
<a class="u-url permalink" href={{ .RelPermalink }}>Permalink</a>
|
||||
<div class="e-content">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
</article>
|
||||
<div id="webmentions"></div>
|
||||
</div>
|
||||
{{ end }}
|
|
@ -0,0 +1,16 @@
|
|||
{{ define "main" }}
|
||||
<div class="h-card idx-card">
|
||||
<div id="sidebar">
|
||||
<a class="u-url" href="https://yequari.com"><img class="u-photo" src="/images/starscream_d.png"></a>
|
||||
</div>
|
||||
<div id="content">
|
||||
<p class="p-note">
|
||||
Hello I'm <span class="p-name">yequari</span>. I like to code, play TCGs, and collect Transformers. The Internet is a busy place, so thanks for checking out my small corner of it. Come in and take a break!
|
||||
</p>
|
||||
<p>
|
||||
<div id="statuscafe"><div id="statuscafe-username"></div><div id="statuscafe-content"></div></div><script src="https://status.cafe/current-status.js?name=yequari" defer></script>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{{ end }}
|
|
@ -0,0 +1,15 @@
|
|||
<div id="footer">
|
||||
<div id="quote">
|
||||
<p id="subtitle">.</p>
|
||||
</div>
|
||||
<div id="social-links">
|
||||
<ul class="social">
|
||||
<li class="social"><a rel="me" href="https://social.yesterweb.org/@yequari">Mastodon</a></li> |
|
||||
<li class="social"><a rel="me" href="https://twitter.com/yequari">Twitter</a></li> |
|
||||
<li class="social"><a href="/blog/atom.xml">RSS</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="yw-widget-text yw-raw" data-yw-url="https://yequari.com">
|
||||
<script src="https://yesterweb.org/js/widget.js"></script>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,13 @@
|
|||
<head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta charset="utf-8">
|
||||
<title>{{ .Site.Title }}</title>
|
||||
<link rel="preconnect" href="https://fonts.bunny.net">
|
||||
<link href="https://fonts.bunny.net/css?family=asar:400|bungee:400|nova-mono:400" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="/css/main.css">
|
||||
<link rel="icon" type="image/png" href="/static/favicon.png">
|
||||
<script src="/js/quotes.js"></script>
|
||||
<link rel="webmention" href="https://webmention.io/yequari.com/webmention" />
|
||||
<link rel="pingback" href="https://webmention.io/yequari.com/xmlrpc" />
|
||||
<script src="/js/webmention.js" async></script>
|
||||
</head>
|
|
@ -0,0 +1,15 @@
|
|||
<div id="header">
|
||||
<div class="titlex">
|
||||
<a href="https://yequari.com/"><h1>yequari</h1></a>
|
||||
</div>
|
||||
<div class="nav">
|
||||
<ul class="nav">
|
||||
<li class="nav"><a href="/">HOME</a></li>
|
||||
<li class="nav"><a href="/posts">BLOG</a></li>
|
||||
<li class="nav"><a href="/tech">TECH</a></li>
|
||||
<li class="nav"><a href="/transformers">TRANSFORMERS</a></li>
|
||||
<li class="nav"><a href="/now">ABOUT</a></li>
|
||||
<li class="nav"><a href="/links">LINKS</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,324 @@
|
|||
/* CSS HEX */
|
||||
:root {
|
||||
--jet: #353535ff;
|
||||
--chinese-red: #aa3322ff;
|
||||
--pale-silver: #c4bbafff;
|
||||
--space-cadet: #292640ff;
|
||||
--xiketic: #0f101aff;
|
||||
--blue-ryb: #4056f4ff;
|
||||
--raisin-black: #241e1eff;
|
||||
--dim-gray: #756b6bff;
|
||||
--pewter-blue: #93A8ACff;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Genesis";
|
||||
src: url("/fonts/NiseGenesis.TTF");
|
||||
}
|
||||
|
||||
/*
|
||||
* Page text: Asar
|
||||
* Headings & Top nav: Syne Mono
|
||||
*/
|
||||
|
||||
body {
|
||||
background-color: var(--jet);
|
||||
color: var(--pale-silver);
|
||||
font-family: 'Asar', serif;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
body::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1.1rem;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
a:link, a:visited {
|
||||
transition-property: color;
|
||||
transition-duration: 0.5s;
|
||||
transition-delay: 0.1s;
|
||||
color: var(--chinese-red);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover, a:active {
|
||||
color: var(--pewter-blue);
|
||||
}
|
||||
|
||||
h1,h2,h3,h4,h5,h6 {
|
||||
color: var(--pewter-blue);
|
||||
font-family: 'Bungee', display;
|
||||
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
max-width: 800px;
|
||||
margin: 10px auto;
|
||||
background-color: var(--raisin-black);
|
||||
padding: 0 20px;
|
||||
border: 4px double var(--dim-gray);
|
||||
border-bottom-color: var(--chinese-red);
|
||||
border-radius: 3px;
|
||||
min-height: 800px;
|
||||
}
|
||||
|
||||
#header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
margin: 100px auto 10px auto;
|
||||
padding: 0;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
/* gap: 20px; */
|
||||
z-index: 1;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
min-width: 250px;
|
||||
flex: 1 1 33%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#sidebar img {
|
||||
padding: 2em 2em;
|
||||
}
|
||||
|
||||
#content {
|
||||
flex: 1 1 66%;
|
||||
padding: 0;
|
||||
z-index: 1;
|
||||
margin: 0px auto;
|
||||
}
|
||||
|
||||
#content::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-top: 1px solid var(--pale-silver);
|
||||
padding: 0 0 15px 0px;
|
||||
text-align: center;
|
||||
color: var(--dim-gray)
|
||||
}
|
||||
|
||||
#quote {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#social-links {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
div.nav {
|
||||
flex: 1;
|
||||
height: 50px;
|
||||
border: 2px solid var(--pale-silver);
|
||||
border-radius: 40px;
|
||||
margin: 10px auto;
|
||||
padding: 1px 20px;
|
||||
}
|
||||
|
||||
ul.nav {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
li.nav {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
li.nav a {
|
||||
display: inline-block;
|
||||
padding: 15px 20px;
|
||||
transition-property: background-color;
|
||||
transition-duration: 0.5s;
|
||||
transition-delay: 0.1s;
|
||||
font-family: 'Nova Mono', monospace;
|
||||
font-weight: bold;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
li.nav a:hover, li.nav a:active {
|
||||
background-color: var(--jet);
|
||||
color: var(--chinese-red);
|
||||
}
|
||||
|
||||
div.titlex h1 {
|
||||
font-family: "Genesis", 'Courier New', Courier, monospace;
|
||||
color: var(--pewter-blue);
|
||||
}
|
||||
|
||||
ul.social {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
color: var(--jet)
|
||||
}
|
||||
|
||||
li.social {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
li.social a {
|
||||
display: inline-block;
|
||||
padding: 0 10px;
|
||||
color: var(--jet);
|
||||
}
|
||||
|
||||
a.permalink {
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
a.summary {
|
||||
color: var(--pale-silver);
|
||||
}
|
||||
|
||||
time.dt-published {
|
||||
color: var(--dim-gray);
|
||||
}
|
||||
|
||||
ul.posts {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 500px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: black;
|
||||
padding: 10px;
|
||||
overflow-x: scroll;
|
||||
color: white;
|
||||
}
|
||||
|
||||
pre code{
|
||||
display: block;
|
||||
}
|
||||
|
||||
#webmentions img { max-height: 1.2em; margin-right: -1ex; }
|
||||
|
||||
.blognav {
|
||||
text-align: right;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/*
|
||||
decorative stuff :)
|
||||
*/
|
||||
|
||||
#vent {
|
||||
width: 200px;
|
||||
height: 410px;
|
||||
background-color: var(--raisin-black);
|
||||
background-image: url("/images/gen-texture1.png");
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 30px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Links page
|
||||
*/
|
||||
|
||||
#links-content {
|
||||
min-width: 0;
|
||||
margin: 0px auto;
|
||||
}
|
||||
|
||||
#links-main {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
|
||||
#links-main h4 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
#links-main li {
|
||||
padding: 7px 0;
|
||||
}
|
||||
|
||||
.links-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 33%;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
#links-bottom {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.idx-card {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
|
||||
.hover-links {
|
||||
position: relative;
|
||||
border-bottom: 1px dotted var(--chinese-red);
|
||||
}
|
||||
|
||||
.hover-links:before {
|
||||
content: attr(data-hover);
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
max-width: 340px;
|
||||
min-width: 200px;
|
||||
background-color: var(--xiketic);
|
||||
color: var(--pewter-blue);
|
||||
text-align: center;
|
||||
border: 4px double var(--dim-gray);
|
||||
border-radius: 5px;
|
||||
padding: 5px;
|
||||
transition: opacity 1s ease-in-out;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
left: -50px;
|
||||
top: 110%;
|
||||
}
|
||||
|
||||
.hover-links:hover:before {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 500px) {
|
||||
div.nav {
|
||||
border: none;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
var subtitles = [
|
||||
"null",
|
||||
"George likes his chicken spicy",
|
||||
"Graphic design is my passion",
|
||||
"They don\'t think it be like it is, but it do",
|
||||
"Terminally online",
|
||||
"Twitter is a fandom site for current events",
|
||||
"Genesis does what Nintendon\'t",
|
||||
"1000 JS libraries in your pocket",
|
||||
"A large boulder the size of a small boulder"
|
||||
];
|
||||
var index = Math.floor(Math.random() * subtitles.length);
|
||||
console.log("setting subtitle to " + subtitles[index]);
|
||||
window.onload = function() {
|
||||
document.getElementById("subtitle").textContent = subtitles[index];
|
||||
}
|
|
@ -0,0 +1,458 @@
|
|||
/* webmention.js
|
||||
|
||||
Simple thing for embedding webmentions from webmention.io into a page, client-side.
|
||||
|
||||
(c)2018-2022 fluffy (http://beesbuzz.biz)
|
||||
|
||||
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.
|
||||
|
||||
GitHub repo (for latest released versions, issue tracking, etc.):
|
||||
|
||||
https://github.com/PlaidWeb/webmention.js
|
||||
|
||||
Basic usage:
|
||||
|
||||
<script src="/path/to/webmention.js" data-param="val" ... async />
|
||||
<div id="webmentions"></div>
|
||||
|
||||
Allowed parameters:
|
||||
|
||||
page-url:
|
||||
|
||||
The base URL to use for this page. Defaults to window.location
|
||||
|
||||
add-urls:
|
||||
|
||||
Additional URLs to check, separated by |s
|
||||
|
||||
id:
|
||||
|
||||
The HTML ID for the object to fill in with the webmention data.
|
||||
Defaults to "webmentions"
|
||||
|
||||
wordcount:
|
||||
|
||||
The maximum number of words to render in reply mentions.
|
||||
|
||||
max-webmentions:
|
||||
|
||||
The maximum number of mentions to retrieve. Defaults to 30.
|
||||
|
||||
prevent-spoofing:
|
||||
|
||||
By default, Webmentions render using the mf2 'url' element, which plays
|
||||
nicely with webmention bridges (such as brid.gy and telegraph)
|
||||
but allows certain spoofing attacks. If you would like to prevent
|
||||
spoofing, set this to a non-empty string (e.g. "true").
|
||||
|
||||
sort-by:
|
||||
|
||||
What to order the responses by; defaults to 'published'. See
|
||||
https://github.com/aaronpk/webmention.io#api
|
||||
|
||||
sort-dir:
|
||||
|
||||
The order to sort the responses by; defaults to 'up' (i.e. oldest
|
||||
first). See https://github.com/aaronpk/webmention.io#api
|
||||
|
||||
comments-are-reactions:
|
||||
|
||||
If set to a non-empty string (e.g. "true"), will display comment-type responses
|
||||
(replies/mentions/etc.) as being part of the reactions
|
||||
(favorites/bookmarks/etc.) instead of in a separate comment list.
|
||||
|
||||
A more detailed example:
|
||||
|
||||
<!-- If you want to translate the UI -->
|
||||
<script src="/path/to/umd/i18next.js"></script>
|
||||
<script>
|
||||
// Setup i18next as described in https://www.i18next.com/overview/getting-started#basic-sample
|
||||
</script>
|
||||
<!-- Otherwise, only using the following is fine -->
|
||||
<script src="/path/to/webmention.min.js"
|
||||
data-id="webmentionContainer"
|
||||
data-wordcount="30"
|
||||
data-prevent-spoofing="true"
|
||||
data-comments-are-reactions="true"
|
||||
/>
|
||||
|
||||
*/
|
||||
|
||||
// Begin LibreJS code licensing
|
||||
// @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
// Shim i18next
|
||||
window.i18next = window.i18next || {
|
||||
t: function t(/** @type {string} */key) { return key; }
|
||||
}
|
||||
const t = window.i18next.t.bind(window.i18next);
|
||||
|
||||
/**
|
||||
* Read the configuration value.
|
||||
*
|
||||
* @param {string} key The configuration key.
|
||||
* @param {string} dfl The default value.
|
||||
* @returns {string}
|
||||
*/
|
||||
function getCfg(key, dfl) {
|
||||
return document.currentScript.getAttribute("data-" + key) || dfl;
|
||||
}
|
||||
|
||||
const refurl = getCfg("page-url", window.location.href.replace(/#.*$/, ""));
|
||||
const addurls = getCfg("add-urls", undefined);
|
||||
const containerID = getCfg("id", "webmentions");
|
||||
/** @type {Number} */
|
||||
const textMaxWords = getCfg("wordcount");
|
||||
const maxWebmentions = getCfg("max-webmentions", 30);
|
||||
const mentionSource = getCfg("prevent-spoofing") ? "wm-source" : "url";
|
||||
const sortBy = getCfg("sort-by", "published");
|
||||
const sortDir = getCfg("sort-dir", "up");
|
||||
/** @type {boolean} */
|
||||
const commentsAreReactions = getCfg("comments-are-reactions");
|
||||
|
||||
/**
|
||||
* @typedef MentionType
|
||||
* @type {"in-reply-to"|"like-of"|"repost-of"|"bookmark-of"|"mention-of"|"rsvp"|"follow-of"}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Maps a reaction to a hover title.
|
||||
*
|
||||
* @type {Record<MentionType, string>}
|
||||
*/
|
||||
const reactTitle = {
|
||||
"in-reply-to": t("replied"),
|
||||
"like-of": t("liked"),
|
||||
"repost-of": t("reposted"),
|
||||
"bookmark-of": t("bookmarked"),
|
||||
"mention-of": t("mentioned"),
|
||||
"rsvp": t("RSVPed"),
|
||||
"follow-of": t("followed")
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps a reaction to an emoji.
|
||||
*
|
||||
* @type {Record<MentionType, string>}
|
||||
*/
|
||||
const reactEmoji = {
|
||||
"in-reply-to": "💬",
|
||||
"like-of": "❤️",
|
||||
"repost-of": "🔄",
|
||||
"bookmark-of": "⭐️",
|
||||
"mention-of": "💬",
|
||||
"rsvp": "📅",
|
||||
"follow-of": "🐜"
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef RSVPEmoji
|
||||
* @type {"yes"|"no"|"interested"|"maybe"|null}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Maps a RSVP to an emoji.
|
||||
*
|
||||
* @type {Record<RSVPEmoji, string>}
|
||||
*/
|
||||
const rsvpEmoji = {
|
||||
"yes": "✅",
|
||||
"no": "❌",
|
||||
"interested": "💡",
|
||||
"maybe": "💭"
|
||||
};
|
||||
|
||||
/**
|
||||
* HTML escapes the string.
|
||||
*
|
||||
* @param {string} text The string to be escaped.
|
||||
* @returns {string}
|
||||
*/
|
||||
function entities(text) {
|
||||
return text.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the markup for an reaction image.
|
||||
*
|
||||
* @param {Reaction} r
|
||||
* @param {boolean} isComment
|
||||
* @returns {string}
|
||||
*/
|
||||
function reactImage(r, isComment) {
|
||||
const who = entities(
|
||||
r.author?.name || r.url.split("/")[2]
|
||||
);
|
||||
/** @type {string} */
|
||||
let response = reactTitle[r["wm-property"]] || t("reacted");
|
||||
if (!isComment && r.content && r.content.text) {
|
||||
response += ": " + extractComment(r);
|
||||
}
|
||||
|
||||
let authorPhoto = '';
|
||||
if (r.author && r.author.photo) {
|
||||
authorPhoto = `
|
||||
<img
|
||||
src="${entities(r.author.photo)}"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
alt="${who}"
|
||||
>
|
||||
`;
|
||||
}
|
||||
|
||||
let rsvp = '';
|
||||
if (r.rsvp && rsvpEmoji[r.rsvp]) {
|
||||
rsvp = `<sub>${rsvpEmoji[r.rsvp]}</sub>`;
|
||||
}
|
||||
|
||||
return`
|
||||
<a
|
||||
class="reaction"
|
||||
rel="nofollow ugc"
|
||||
title="${who} ${response}"
|
||||
href="${r[mentionSource]}"
|
||||
>
|
||||
${authorPhoto}
|
||||
${(reactEmoji[r['wm-property']] || '💥')}
|
||||
${rsvp}
|
||||
</a>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip the protocol off a URL.
|
||||
*
|
||||
* @param {string} url The URL to strip protocol off.
|
||||
* @returns {string}
|
||||
*/
|
||||
function stripurl(url) {
|
||||
return url.substr(url.indexOf('//'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deduplicate multiple mentions from the same source URL.
|
||||
*
|
||||
* @param {Array<Reaction>} mentions Mentions of the source URL.
|
||||
* @return {Array<Reaction>}
|
||||
*/
|
||||
function dedupe(mentions) {
|
||||
/** @type {Array<Reaction>} */
|
||||
const filtered = [];
|
||||
/** @type {Record<string, boolean>} */
|
||||
const seen = {};
|
||||
|
||||
mentions.forEach(function(r) {
|
||||
// Strip off the protocol (i.e. treat http and https the same)
|
||||
const source = stripurl(r.url);
|
||||
if (!seen[source]) {
|
||||
filtered.push(r);
|
||||
seen[source] = true;
|
||||
}
|
||||
});
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract comments from a reaction.
|
||||
*
|
||||
* @param {Reactions} c
|
||||
* @returns string
|
||||
*/
|
||||
function extractComment(c) {
|
||||
let text = entities(c.content.text);
|
||||
|
||||
if (textMaxWords) {
|
||||
let words = text.replace(/\s+/g,' ').split(' ', textMaxWords + 1);
|
||||
if (words.length > textMaxWords) {
|
||||
words[textMaxWords - 1] += '…';
|
||||
words = words.slice(0, textMaxWords);
|
||||
text = words.join(' ');
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format comments as HTML.
|
||||
*
|
||||
* @param {Array<Reaction>} comments The comments to format.
|
||||
* @returns string
|
||||
*/
|
||||
function formatComments(comments) {
|
||||
const headline = `<h2>${t('Responses')}</h2>`;
|
||||
const markup = comments
|
||||
.map((c) => {
|
||||
const image = reactImage(c, true);
|
||||
|
||||
let source = entities(c.url.split('/')[2]);
|
||||
if (c.author && c.author.name) {
|
||||
source = entities(c.author.name);
|
||||
}
|
||||
const link = `<a class="source" rel="nofollow ugc" href="${c[mentionSource]}">${source}</a>`;
|
||||
|
||||
let linkclass = "name";
|
||||
let linktext = `(${t("mention")})`;
|
||||
if (c.name) {
|
||||
linkclass = "name";
|
||||
linktext = c.name;
|
||||
} else if (c.content && c.content.text) {
|
||||
linkclass = "text";
|
||||
linktext = extractComment(c);
|
||||
}
|
||||
|
||||
const type = `<span class="${linkclass}">${linktext}</span>`;
|
||||
|
||||
return `<li>${image} ${link} ${type}</li>`;
|
||||
})
|
||||
.join('');
|
||||
return `
|
||||
${headline}
|
||||
<ul class="comments">${markup}</ul>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} Reaction
|
||||
* @property {string} url
|
||||
* @property {Object?} author
|
||||
* @property {string?} author.name
|
||||
* @property {string?} author.photo
|
||||
* @property {Object?} content
|
||||
* @property {string?} content.text
|
||||
* @property {RSVPEmoji?} rsvp
|
||||
* @property {MentionType?} wm-property
|
||||
* @property {string?} wm-source
|
||||
*/
|
||||
|
||||
/**
|
||||
* Formats a list of reactions as HTML.
|
||||
*
|
||||
* @param {Array<Reaction>} reacts List of reactions to format
|
||||
* @returns string
|
||||
*/
|
||||
function formatReactions(reacts) {
|
||||
const headline = `<h2>${t('Reactions')}</h2>`;
|
||||
|
||||
const markup = reacts.map((r) => reactImage(r)).join('');
|
||||
|
||||
return `
|
||||
${headline}
|
||||
<ul class="reacts">${markup}</ul>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef WebmentionResponse
|
||||
* @type {Object}
|
||||
* @property {Array<Reaction>} children
|
||||
*/
|
||||
|
||||
/**
|
||||
* Register event listener.
|
||||
*/
|
||||
window.addEventListener("load", async function () {
|
||||
const container = document.getElementById(containerID);
|
||||
if (!container) {
|
||||
// no container, so do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
const pages = [stripurl(refurl)];
|
||||
if (!!addurls) {
|
||||
addurls.split('|').forEach(function (url) {
|
||||
pages.push(stripurl(url));
|
||||
});
|
||||
}
|
||||
|
||||
let apiURL = `https://webmention.io/api/mentions.jf2?per-page=${maxWebmentions}&sort-by=${sortBy}&sort-dir=${sortDir}`;
|
||||
|
||||
pages.forEach(function (path) {
|
||||
apiURL += `&target[]=${encodeURIComponent('http:' + path)}&target[]=${encodeURIComponent('https:' + path)}`;
|
||||
});
|
||||
|
||||
/** @type {WebmentionResponse} */
|
||||
let json = {};
|
||||
try {
|
||||
const response = await window.fetch(apiURL);
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
json = await response.json();
|
||||
} else {
|
||||
console.error("Could not parse response");
|
||||
new Error(response.statusText);
|
||||
}
|
||||
} catch(error) {
|
||||
// Purposefully not escalate further, i.e. no UI update
|
||||
console.error("Request failed", error);
|
||||
}
|
||||
|
||||
/** @type {Array<Reaction>} */
|
||||
let comments = [];
|
||||
/** @type {Array<Reaction>} */
|
||||
const collects = [];
|
||||
|
||||
if (commentsAreReactions) {
|
||||
comments = collects;
|
||||
}
|
||||
|
||||
/** @type {Record<MentionType, Array<Reaction>>} */
|
||||
const mapping = {
|
||||
"in-reply-to": comments,
|
||||
"like-of": collects,
|
||||
"repost-of": collects,
|
||||
"bookmark-of": collects,
|
||||
"follow-of": collects,
|
||||
"mention-of": comments,
|
||||
"rsvp": comments
|
||||
};
|
||||
|
||||
json.children.forEach(function(child) {
|
||||
// Map each mention into its respective container
|
||||
const store = mapping[child['wm-property']];
|
||||
if (store) {
|
||||
store.push(child);
|
||||
}
|
||||
});
|
||||
|
||||
// format the comment-type things
|
||||
let formattedComments = '';
|
||||
if (comments.length > 0 && comments !== collects) {
|
||||
formattedComments = formatComments(dedupe(comments));
|
||||
}
|
||||
|
||||
// format the other reactions
|
||||
let reactions = '';
|
||||
if (collects.length > 0) {
|
||||
reactions = formatReactions(dedupe(collects));
|
||||
}
|
||||
|
||||
container.innerHTML = `${formattedComments}${reactions}`;
|
||||
});
|
||||
}());
|
||||
|
||||
// End-of-file marker for LibreJS
|
||||
// @license-end
|
|
@ -0,0 +1,21 @@
|
|||
# theme.toml template for a Hugo theme
|
||||
# See https://github.com/gohugoio/hugoThemes#themetoml for an example
|
||||
|
||||
name = "Genesis"
|
||||
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 = ""
|