Added all posts
This commit is contained in:
parent
5e2141941b
commit
ac8bab9e38
97
App.go
97
App.go
@ -4,6 +4,8 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.akyoto.dev/go/markdown"
|
||||
@ -13,12 +15,12 @@ import (
|
||||
|
||||
type App struct {
|
||||
html string
|
||||
posts map[string]string
|
||||
posts map[string]*Post
|
||||
}
|
||||
|
||||
func (app *App) Init() {
|
||||
app.html = mustLoadClean("public/app.html")
|
||||
css := mustLoadClean("public/app.css")
|
||||
app.html = loadClean("public/app.html")
|
||||
css := loadClean("public/app.css")
|
||||
app.html = strings.Replace(app.html, "{head}", fmt.Sprintf("{head}<style>%s</style>", css), 1)
|
||||
app.posts = loadPosts("posts")
|
||||
}
|
||||
@ -56,31 +58,59 @@ func (app *App) Run() {
|
||||
}
|
||||
|
||||
s.Get("/", func(ctx web.Context) error {
|
||||
md := bytes.Buffer{}
|
||||
html := bytes.Buffer{}
|
||||
html.WriteString(`<h2>Blog</h2><ul class="blog">`)
|
||||
articles := []*Post{}
|
||||
|
||||
for slug := range app.posts {
|
||||
fmt.Fprintf(&md, "- [%s](/%s)\n", slug, slug)
|
||||
for _, post := range app.posts {
|
||||
if !post.Published || !slices.Contains(post.Tags, "article") {
|
||||
continue
|
||||
}
|
||||
|
||||
articles = append(articles, post)
|
||||
}
|
||||
|
||||
return render(ctx, "<title>akyoto.dev</title>", markdown.Render(md.String()))
|
||||
sort.Slice(articles, func(i, j int) bool {
|
||||
return articles[i].Created > articles[j].Created
|
||||
})
|
||||
|
||||
for _, post := range articles {
|
||||
fmt.Fprintf(&html, `<li><a href="/%s">%s</a><time datetime="%s">%s</time></li>`, post.Slug, post.Title, post.Created, post.Created[:len("YYYY")])
|
||||
}
|
||||
|
||||
html.WriteString(`</ul>`)
|
||||
return render(ctx, "<title>akyoto.dev</title>", html.String())
|
||||
})
|
||||
|
||||
s.Get("/:post", func(ctx web.Context) error {
|
||||
post := ctx.Request().Param("post")
|
||||
return render(ctx, "<title>akyoto.dev</title>", app.posts[post])
|
||||
slug := ctx.Request().Param("post")
|
||||
post := app.posts[slug]
|
||||
head := fmt.Sprintf(`<title>%s</title><meta name="keywords" content="%s">`, post.Title, strings.Join(post.Tags, ","))
|
||||
content := ""
|
||||
|
||||
if slices.Contains(post.Tags, "article") {
|
||||
content = fmt.Sprintf(
|
||||
`<article><header><h1>%s</h1><time datetime="%s">%s</time></header>%s</article>`,
|
||||
post.Title,
|
||||
post.Created,
|
||||
post.Created[:len("YYYY-MM-DD")],
|
||||
markdown.Render(post.Content),
|
||||
)
|
||||
} else {
|
||||
content = fmt.Sprintf(
|
||||
`<h2>%s</h2>%s`,
|
||||
post.Title,
|
||||
markdown.Render(post.Content),
|
||||
)
|
||||
}
|
||||
|
||||
return render(ctx, head, content)
|
||||
})
|
||||
|
||||
s.Run(":8080")
|
||||
}
|
||||
|
||||
func mustLoadClean(path string) string {
|
||||
data := mustLoad(path)
|
||||
data = strings.ReplaceAll(data, "\t", "")
|
||||
data = strings.ReplaceAll(data, "\n", "")
|
||||
return data
|
||||
}
|
||||
|
||||
func mustLoad(path string) string {
|
||||
func load(path string) string {
|
||||
dataBytes, err := os.ReadFile(path)
|
||||
|
||||
if err != nil {
|
||||
@ -90,32 +120,9 @@ func mustLoad(path string) string {
|
||||
return string(dataBytes)
|
||||
}
|
||||
|
||||
func loadPosts(directory string) map[string]string {
|
||||
entries, err := os.ReadDir(directory)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
posts := map[string]string{}
|
||||
|
||||
for _, entry := range entries {
|
||||
fileName := entry.Name()
|
||||
|
||||
if !strings.HasSuffix(fileName, ".md") {
|
||||
continue
|
||||
}
|
||||
|
||||
baseName := strings.TrimSuffix(fileName, ".md")
|
||||
content := mustLoad("posts/" + fileName)
|
||||
|
||||
if strings.HasPrefix(content, "---\n") {
|
||||
end := strings.Index(content[4:], "---\n") + 4
|
||||
content = content[end+4:]
|
||||
}
|
||||
|
||||
posts[baseName] = markdown.Render(content)
|
||||
}
|
||||
|
||||
return posts
|
||||
func loadClean(path string) string {
|
||||
data := load(path)
|
||||
data = strings.ReplaceAll(data, "\t", "")
|
||||
data = strings.ReplaceAll(data, "\n", "")
|
||||
return data
|
||||
}
|
||||
|
78
Post.go
Normal file
78
Post.go
Normal file
@ -0,0 +1,78 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Post struct {
|
||||
Slug string
|
||||
Content string
|
||||
Title string
|
||||
Tags []string
|
||||
Created string
|
||||
Published bool
|
||||
}
|
||||
|
||||
func loadPosts(directory string) map[string]*Post {
|
||||
entries, err := os.ReadDir(directory)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
posts := map[string]*Post{}
|
||||
|
||||
for _, entry := range entries {
|
||||
fileName := entry.Name()
|
||||
|
||||
if !strings.HasSuffix(fileName, ".md") {
|
||||
continue
|
||||
}
|
||||
|
||||
baseName := strings.TrimSuffix(fileName, ".md")
|
||||
content := load("posts/" + fileName)
|
||||
|
||||
post := &Post{
|
||||
Slug: baseName,
|
||||
}
|
||||
|
||||
if strings.HasPrefix(content, "---\n") {
|
||||
end := strings.Index(content[4:], "---\n") + 4
|
||||
frontmatter := content[4:end]
|
||||
content = content[end+4:]
|
||||
|
||||
parseFrontmatter(frontmatter, func(key, value string) {
|
||||
switch key {
|
||||
case "title":
|
||||
post.Title = value
|
||||
case "tags":
|
||||
post.Tags = strings.Split(value, " ")
|
||||
case "created":
|
||||
post.Created = value
|
||||
case "published":
|
||||
post.Published = value == "true"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
post.Content = content
|
||||
posts[baseName] = post
|
||||
}
|
||||
|
||||
return posts
|
||||
}
|
||||
|
||||
func parseFrontmatter(frontmatter string, assign func(key string, value string)) {
|
||||
lines := strings.Split(frontmatter, "\n")
|
||||
|
||||
for _, line := range lines {
|
||||
colon := strings.Index(line, ":")
|
||||
|
||||
if colon == -1 {
|
||||
continue
|
||||
}
|
||||
|
||||
assign(line[:colon], strings.TrimSpace(line[colon+1:]))
|
||||
}
|
||||
}
|
66
posts/about.md
Normal file
66
posts/about.md
Normal file
@ -0,0 +1,66 @@
|
||||
---
|
||||
title: About
|
||||
tags: about
|
||||
created: 2023-07-09T20:04:03Z
|
||||
published: true
|
||||
---
|
||||
|
||||
Hi 👋
|
||||
|
||||
I'm Eduard Urbach, a software developer trying to create high quality [open source](https://git.akyoto.dev/explore/repos) software solutions.
|
||||
|
||||
## Philosophy
|
||||
|
||||
In an age where technology is meant to simplify our lives, it's ironic how often we find ourselves grappling with bloated software - programs that are swollen with unnecessary features, bogged down by excessive complexity, and burdened with what feels like digital excess baggage.
|
||||
Welcome to the era of feature creep, where software developers seem to prioritize quantity over quality, overwhelming users with an avalanche of functionalities that they never asked for nor needed.
|
||||
|
||||
As a wise man once said:
|
||||
|
||||
> Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.
|
||||
|
||||
We shouldn't ask ourselves how much we can add, but how much we can remove, while still maintaining the core task of the software. This leads to much simpler and easy to understand code. I'll quote Terry Davis:
|
||||
|
||||
> An idiot admires complexity, a genius admires simplicity.
|
||||
|
||||
Making a topic look complex is easy, but making it simple and easy to understand is what truly takes skill. Instead of complex and over-engineered solutions that make us "feel intelligent" just because they're complex we should strive for simplicity.
|
||||
|
||||
## Languages
|
||||
|
||||
One of the more unique facts about my life is that I spent 8 years in Asia, more specifically Japan and Korea. This helped me broaden the pool of languages I can utilize to communicate and it also gave me an understanding of the subtle differences between Western and Eastern cultures.
|
||||
|
||||
Currently I speak:
|
||||
|
||||
| | |
|
||||
| ----------- | -------------------------------- |
|
||||
| 🇬🇧 English | - |
|
||||
| 🇩🇪 German | Fußbodenschleifmaschinenverleih. |
|
||||
| 🇯🇵 Japanese | 仕方ないよね〜 |
|
||||
| 🇰🇷 Korean | 두유 노 김치? |
|
||||
| 🇷🇺 Russian | давай давай! |
|
||||
|
||||
## Software
|
||||
|
||||
I like [Arch Linux](https://archlinux.org) as my operating system for multiple reasons:
|
||||
|
||||
- minimal system
|
||||
- rolling release
|
||||
- pacman is awesome
|
||||
- wiki is amazing
|
||||
|
||||
My desktop is now based on [Hyprland](https://hyprland.org) after having used [Gnome Shell](https://gnome.org) for the past 10 years.
|
||||
|
||||
I use [Neovim](https://neovim.io) as my lightweight editor and [VS Code](https://code.visualstudio.com) only if it has better plugins for the project I'm working on.
|
||||
|
||||
I am currently experimenting with [fish](https://fishshell.com) as my daily shell replacing [zsh](https://zsh.org).
|
||||
|
||||
## Community
|
||||
|
||||
In case you want to contribute to any projects, join [#community:akyoto.dev](https://matrix.to/#/#community:akyoto.dev) with a client you like.
|
||||
I recommend [Cinny](https://cinny.in/) because it has a beautiful design and closely resembles Discord.
|
||||
|
||||
## Donations
|
||||
|
||||
- [Kofi](https://ko-fi.com/akyoto)
|
||||
- [LiberaPay](https://liberapay.com/akyoto/donate)
|
||||
- [PayPal](https://www.paypal.com/donate/?hosted_button_id=EUBC5TT82APK6)
|
||||
- [Stripe](https://donate.stripe.com/28o9Dn2xlehb6Zi8ww)
|
60
posts/blender.md
Normal file
60
posts/blender.md
Normal file
@ -0,0 +1,60 @@
|
||||
---
|
||||
title: Blender
|
||||
tags: note troubleshooting
|
||||
created: 2024-02-05T14:03:13Z
|
||||
published: true
|
||||
---
|
||||
|
||||
## Basics
|
||||
|
||||
| | |
|
||||
| ---------------- | --------- |
|
||||
| Add | Shift A |
|
||||
| Grab | G |
|
||||
| Rotate | R |
|
||||
| Scale | S |
|
||||
| Extrude | E |
|
||||
| Inset | I |
|
||||
| Bevel | Ctrl B |
|
||||
| Loop cut | Ctrl R |
|
||||
| Fill | F |
|
||||
| Merge | M |
|
||||
| Apply | Ctrl A |
|
||||
| Join | Ctrl J |
|
||||
| Parent | Ctrl P |
|
||||
| Grab on X axis | G X |
|
||||
| Grab on YZ plane | G Shift X |
|
||||
| Slide | G G |
|
||||
|
||||
## Vision
|
||||
|
||||
| | |
|
||||
| -------------- | ------- |
|
||||
| Hide | H |
|
||||
| Unhide | Alt H |
|
||||
| Focus | Num . |
|
||||
| Focus region | Shift B |
|
||||
| Focus all | Home |
|
||||
| Isolate | / |
|
||||
| Viewpoint menu | ~ |
|
||||
|
||||
## Modes
|
||||
|
||||
| | |
|
||||
| ----------- | --- |
|
||||
| Vertex mode | 1 |
|
||||
| Edge mode | 2 |
|
||||
| Face mode | 3 |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### How to rename something?
|
||||
|
||||
You can rename objects with F2 but in many other places you need to double click the name with your mouse.
|
||||
|
||||
### What is neutral_bone?
|
||||
|
||||
When you export a rigged model with Blender in glTF format, you will sometimes notice a `neutral_bone` in the exported data.
|
||||
This is because some vertices were not assigned a vertex group.
|
||||
|
||||
In edit mode, go to `Select > Select all by trait > Ungrouped vertices` to find the problematic vertices and delete them.
|
17
posts/contact.md
Normal file
17
posts/contact.md
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
title: Contact
|
||||
tags: about
|
||||
created: 2023-07-23T12:43:21Z
|
||||
published: true
|
||||
---
|
||||
|
||||
| | |
|
||||
| ------------- | ------------------------------------------------------------ |
|
||||
| Name | Eduard Urbach |
|
||||
| Occupation | Software Engineer |
|
||||
| Business type | Freelancer |
|
||||
| E-Mail | admin [at] akyoto.dev |
|
||||
| Chat | [@akyoto:akyoto.dev](https://matrix.to/#/@akyoto:akyoto.dev) |
|
||||
| Mobile | +49 1520 4296914 |
|
||||
| Tax number | DE 362234011 |
|
||||
| Address | [n/a](https://dserver.bundestag.de/btd/19/077/1907714.pdf) |
|
1912
posts/emoji.md
Normal file
1912
posts/emoji.md
Normal file
File diff suppressed because it is too large
Load Diff
62
posts/environment-variables.md
Normal file
62
posts/environment-variables.md
Normal file
@ -0,0 +1,62 @@
|
||||
---
|
||||
title: Environment Variables
|
||||
tags: article software linux system configuration
|
||||
created: 2024-03-07T10:09:08Z
|
||||
published: true
|
||||
---
|
||||
|
||||
Recently, I had to configure some user specific environment variables for my Linux installation.
|
||||
The problem was that I needed those variables to work in all of my shells and in every desktop environment.
|
||||
Since `.pam_environment` is deprecated and `.profile` isn't sourced by every shell, I needed another solution that works globally.
|
||||
|
||||
## User variables
|
||||
|
||||
One solution is to use `systemd` for this task.
|
||||
You can configure user environment variables in the `~/.config/environment.d` directory.
|
||||
Multiple files can be added and all of them will be processed on session start:
|
||||
|
||||
```
|
||||
.
|
||||
├── 10-xdg.conf
|
||||
├── 20-dirs.conf
|
||||
├── 30-general.conf
|
||||
└── 40-apps.conf
|
||||
```
|
||||
|
||||
The format inside these files is very simple:
|
||||
|
||||
```ini
|
||||
KEY=value
|
||||
KEY=value
|
||||
KEY=value
|
||||
```
|
||||
|
||||
They support variable expansion so it's possible to use things like `$HOME` in the value text.
|
||||
You can take a look at my [environment.d](https://git.akyoto.dev/sys/home/src/branch/main/.config/environment.d) if you need some examples.
|
||||
|
||||
GDM and KDE Plasma will automatically source these variables on session start.
|
||||
If you don't use these or you don't want to rely on them, you need to manually load the variables in your shell config files.
|
||||
|
||||
### bash / zsh
|
||||
|
||||
```bash
|
||||
export $(/usr/lib/systemd/user-environment-generators/30-systemd-environment-d-generator)
|
||||
```
|
||||
|
||||
### fish
|
||||
|
||||
```bash
|
||||
export (/usr/lib/systemd/user-environment-generators/30-systemd-environment-d-generator)
|
||||
```
|
||||
|
||||
Even if you use a graphical environment it makes sense to add these to your shell startup because it allows you to see the changes made to your environment immediately. In case you're worried about execution time, it adds about 3 ms to the shell startup on my machine and is well worth the price.
|
||||
|
||||
## System variables
|
||||
|
||||
Hardware and system specific variables that do not need to live in a version controlled repository can be set in `/etc/environment`. For example, on a system with an Nvidia GPU:
|
||||
|
||||
```ini
|
||||
GBM_BACKEND=nvidia-drm
|
||||
LIBVA_DRIVER_NAME=nvidia
|
||||
__GLX_VENDOR_LIBRARY_NAME=nvidia
|
||||
```
|
64
posts/file-structure.md
Normal file
64
posts/file-structure.md
Normal file
@ -0,0 +1,64 @@
|
||||
---
|
||||
title: File Structure
|
||||
tags: article software
|
||||
created: 2023-07-28T09:21:24Z
|
||||
published: true
|
||||
---
|
||||
|
||||
There are two widely used types of source code organization and I will explain why one of these should be avoided.
|
||||
As an example, let's take a look at the model-view-controller pattern:
|
||||
|
||||
## "by type"
|
||||
|
||||
An inexperienced programmer will often create this file structure when asked to create an MVC project:
|
||||
|
||||
```text
|
||||
.
|
||||
├── controllers
|
||||
│ ├── Post.controller
|
||||
│ └── User.controller
|
||||
├── models
|
||||
│ ├── Post.model
|
||||
│ └── User.model
|
||||
└── views
|
||||
├── Post.view
|
||||
└── User.view
|
||||
```
|
||||
|
||||
We call this "by type" organization and it should be avoided like the devil.
|
||||
|
||||
First of all, it violates the software principle that related things should always stay close to each other.
|
||||
It becomes very hard to find all the files related to a component.
|
||||
Nowadays fuzzy finders can help offset this problem, but it's nonetheless a problem that could have been avoided in the first place instead of requiring extra tooling.
|
||||
|
||||
Secondly, this structure makes deletion non-trivial. If a component is no longer needed, then you need to delete it from your repository.
|
||||
This should be a trivial task, ideally removing one directory and you're done. If your component, however, happens to be spread out over 15 different folders, then the deletion process is very complex.
|
||||
|
||||
## "by feature"
|
||||
|
||||
There is a very simple solution to all of these problems and it's called "by feature" organization:
|
||||
|
||||
```text
|
||||
.
|
||||
├── Post
|
||||
│ ├── Post.controller
|
||||
│ ├── Post.model
|
||||
│ └── Post.view
|
||||
└── User
|
||||
├── User.controller
|
||||
├── User.model
|
||||
└── User.view
|
||||
```
|
||||
|
||||
With this file structure:
|
||||
|
||||
- All related code is closely grouped together
|
||||
- Deleting a component is very easy
|
||||
|
||||
Depending on the style regulations, you might encounter the plural forms `Posts` and `Users`, but it's the same principle. Personally, I prefer singular names because plurals aren't consistent. If your code uses type names via reflection to build the paths, you're better off with singular names.
|
||||
|
||||
## Conclusion
|
||||
|
||||
- Don't organize files by their file type
|
||||
- Organize files by feature
|
||||
- Make deletion simple
|
19
posts/firefox.md
Normal file
19
posts/firefox.md
Normal file
@ -0,0 +1,19 @@
|
||||
---
|
||||
title: Firefox
|
||||
tags: note
|
||||
created: 2024-03-07T18:31:12Z
|
||||
published: false
|
||||
---
|
||||
|
||||
| | |
|
||||
| ----------- | -------------- |
|
||||
| Back | Alt Left |
|
||||
| Forward | Alt Right |
|
||||
| Tab left | Ctrl Shift Tab |
|
||||
| Tab right | Ctrl Tab |
|
||||
| Address bar | Ctrl L |
|
||||
| Web search | Ctrl K |
|
||||
| Quick find | / |
|
||||
| Zoom in | Ctrl + |
|
||||
| Zoom out | Ctrl - |
|
||||
| Zoom reset | Ctrl 0 |
|
76
posts/git.md
Normal file
76
posts/git.md
Normal file
@ -0,0 +1,76 @@
|
||||
---
|
||||
title: Git
|
||||
tags: note software git
|
||||
created: 2023-07-01T08:52:17Z
|
||||
published: true
|
||||
---
|
||||
|
||||
## git
|
||||
|
||||
### Delete branch
|
||||
|
||||
```bash
|
||||
git branch -d $branch
|
||||
```
|
||||
|
||||
### Delete remote branch
|
||||
|
||||
```bash
|
||||
git push origin --delete $branch
|
||||
```
|
||||
|
||||
### Push branch to remote
|
||||
|
||||
```bash
|
||||
git push -u origin $branch
|
||||
```
|
||||
|
||||
### Rebase preserving dates
|
||||
|
||||
```bash
|
||||
git rebase -i --root --committer-date-is-author-date
|
||||
```
|
||||
|
||||
### Rename branch
|
||||
|
||||
```bash
|
||||
git branch -m $old $new
|
||||
```
|
||||
|
||||
### Sign last commit
|
||||
|
||||
```bash
|
||||
git commit --amend --no-edit -S
|
||||
```
|
||||
|
||||
### Show log
|
||||
|
||||
```bash
|
||||
git log --oneline
|
||||
```
|
||||
|
||||
### Show remote
|
||||
|
||||
```bash
|
||||
git remote -v
|
||||
```
|
||||
|
||||
### Undo `git add`
|
||||
|
||||
```bash
|
||||
git reset $file
|
||||
```
|
||||
|
||||
## git lfs
|
||||
|
||||
### Show files
|
||||
|
||||
```bash
|
||||
git lfs ls-files
|
||||
```
|
||||
|
||||
### Show files in the entire history
|
||||
|
||||
```bash
|
||||
git lfs ls-files --all
|
||||
```
|
17
posts/hardware.md
Normal file
17
posts/hardware.md
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
title: Hardware
|
||||
tags: about
|
||||
created: 2023-06-28T08:20:41Z
|
||||
published: true
|
||||
---
|
||||
|
||||
The hardware I'm currently using:
|
||||
|
||||
- MSI Z370-A PRO
|
||||
- Intel Core i7-8700
|
||||
- Nvidia GeForce GTX 1070
|
||||
- 32 GB T-Force DDR4 (4 x 8GB)
|
||||
- 512 GB E5012 M.2 NVMe 2280
|
||||
- Corsair K70 Rapidfire
|
||||
- Logitech G Pro Superlight
|
||||
- BenQ MOBIUZ EX240
|
26
posts/license.md
Normal file
26
posts/license.md
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
title: License
|
||||
tags: legal license
|
||||
created: 2023-08-31T13:46:52Z
|
||||
published: true
|
||||
---
|
||||
|
||||
Copyright © Eduard Urbach
|
||||
|
||||
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.
|
192
posts/linux.md
Normal file
192
posts/linux.md
Normal file
@ -0,0 +1,192 @@
|
||||
---
|
||||
title: Linux
|
||||
tags: note software linux
|
||||
created: 2023-07-01T08:51:23Z
|
||||
published: true
|
||||
---
|
||||
|
||||
The following commands have been tested with Arch Linux.
|
||||
They might work in other distributions with a few modifications.
|
||||
The commands assume standard user permissions and will include `sudo` if root permissions are required.
|
||||
|
||||
## lsof
|
||||
|
||||
### List open TCP connections
|
||||
|
||||
```bash
|
||||
sudo lsof -PniTCP
|
||||
```
|
||||
|
||||
### List open UDP connections
|
||||
|
||||
```bash
|
||||
sudo lsof -PniUDP
|
||||
```
|
||||
|
||||
## nu
|
||||
|
||||
### Sort files and directories by size
|
||||
|
||||
```bash
|
||||
ls -ad | sort-by size | reverse
|
||||
```
|
||||
|
||||
### Filter processes with a high cpu usage
|
||||
|
||||
```bash
|
||||
ps | where cpu > 0 | sort-by cpu | reverse
|
||||
```
|
||||
|
||||
### Filter processes with a high memory usage
|
||||
|
||||
```bash
|
||||
ps | where mem > 30MB | sort-by mem | reverse
|
||||
```
|
||||
|
||||
## openssl
|
||||
|
||||
### Show certificate info
|
||||
|
||||
```bash
|
||||
openssl x509 -text -noout -in $file
|
||||
```
|
||||
|
||||
## pacman
|
||||
|
||||
### Install a package
|
||||
|
||||
```bash
|
||||
sudo pacman -S $package
|
||||
```
|
||||
|
||||
### Uninstall a package and its dependencies
|
||||
|
||||
```bash
|
||||
sudo pacman -Rs $package
|
||||
```
|
||||
|
||||
### List installed packages
|
||||
|
||||
```bash
|
||||
pacman -Q
|
||||
```
|
||||
|
||||
### List orphaned packages
|
||||
|
||||
```bash
|
||||
pacman -Qtdq
|
||||
```
|
||||
|
||||
### Clear cache
|
||||
|
||||
```bash
|
||||
sudo pacman -Sc
|
||||
```
|
||||
|
||||
## rg
|
||||
|
||||
### Search recursively
|
||||
|
||||
```bash
|
||||
rg $text
|
||||
```
|
||||
|
||||
### Search single file
|
||||
|
||||
```bash
|
||||
rg $text $file
|
||||
```
|
||||
|
||||
### Disable all filters
|
||||
|
||||
```bash
|
||||
rg -uuu $text
|
||||
```
|
||||
|
||||
## rsync
|
||||
|
||||
### Sync directory with a server
|
||||
|
||||
```bash
|
||||
rsync -avz $source $destination
|
||||
```
|
||||
|
||||
### Preview changes
|
||||
|
||||
```bash
|
||||
rsync -avz $source $destination --dry-run
|
||||
```
|
||||
|
||||
## systemd
|
||||
|
||||
### List all running services
|
||||
|
||||
```bash
|
||||
systemctl --type=service --state=running
|
||||
```
|
||||
|
||||
### List all enabled services
|
||||
|
||||
```bash
|
||||
systemctl list-unit-files --type=service --state=enabled
|
||||
```
|
||||
|
||||
### Follow log messages
|
||||
|
||||
```bash
|
||||
journalctl -f
|
||||
```
|
||||
|
||||
### Follow log messages for a specific unit
|
||||
|
||||
```bash
|
||||
journalctl -f -u $unit
|
||||
```
|
||||
|
||||
### Show boot time analysis
|
||||
|
||||
```bash
|
||||
systemd-analyze blame
|
||||
```
|
||||
|
||||
### Reboot into BIOS
|
||||
|
||||
```bash
|
||||
systemctl reboot --firmware-setup
|
||||
```
|
||||
|
||||
## tar
|
||||
|
||||
### Compress ze vucking files
|
||||
|
||||
```bash
|
||||
tar czvf $name.tar.gz .
|
||||
```
|
||||
|
||||
### Extract ze vucking files
|
||||
|
||||
```bash
|
||||
tar xzvf $name.tar.gz
|
||||
```
|
||||
|
||||
## users
|
||||
|
||||
### Add a user
|
||||
|
||||
```bash
|
||||
useradd -m $user
|
||||
```
|
||||
|
||||
### Add a group to a user
|
||||
|
||||
```bash
|
||||
usermod -aG $group $user
|
||||
```
|
||||
|
||||
## wmctrl
|
||||
|
||||
### Close all windows
|
||||
|
||||
```bash
|
||||
wmctrl -l | awk '{print $1}' | xargs -rn1 wmctrl -ic
|
||||
```
|
59
posts/neovim.md
Normal file
59
posts/neovim.md
Normal file
@ -0,0 +1,59 @@
|
||||
---
|
||||
title: Neovim
|
||||
tags: note software editor neovim
|
||||
created: 2023-07-01T10:23:16Z
|
||||
published: true
|
||||
---
|
||||
|
||||
## Basics
|
||||
|
||||
| | |
|
||||
| ------------------------------- | -------- |
|
||||
| Normal | `Esc` |
|
||||
| Insert | `i` |
|
||||
| Append after cursor | `a` |
|
||||
| Append at end of line | `A` |
|
||||
| Append line (after) | `o` |
|
||||
| Append line (before) | `O` |
|
||||
| Move to next word | `w` |
|
||||
| Move to beginning of word | `b` |
|
||||
| Move to end of word | `e` |
|
||||
| Move forward to character | `f` |
|
||||
| Move backward to character | `F` |
|
||||
| Move to start of file | `gg` |
|
||||
| Move to end of file | `G` |
|
||||
| Move to start of line | `0` |
|
||||
| Move to end of line | `$` |
|
||||
| Move to first character of line | `^` |
|
||||
| Select | `v` |
|
||||
| Select line | `V` |
|
||||
| Copy | `y` |
|
||||
| Copy line | `yy` |
|
||||
| Paste (before cursor) | `P` |
|
||||
| Paste (after cursor) | `p` |
|
||||
| Delete | `d` |
|
||||
| Delete until end of line | `D` |
|
||||
| Delete line | `dd` |
|
||||
| Repeat last command | `.` |
|
||||
| Undo | `u` |
|
||||
| Redo | `Ctrl r` |
|
||||
| Search | `/` |
|
||||
|
||||
## Commands
|
||||
|
||||
| | |
|
||||
| --------- | ------------- |
|
||||
| Command | `:` |
|
||||
| Edit file | `:e file.txt` |
|
||||
| Replace | `:%s/a/b` |
|
||||
|
||||
## Combinations
|
||||
|
||||
| | |
|
||||
| --------------------------------------- | ------ |
|
||||
| Change word under cursor | `ciw` |
|
||||
| Delete word and whitespace under cursor | `daw` |
|
||||
| Rewrite line | `^Da` |
|
||||
| Select word under cursor | `viw` |
|
||||
| Select next block | `va{` |
|
||||
| Select everything | `ggVG` |
|
35
posts/network-update-rate.md
Normal file
35
posts/network-update-rate.md
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
title: Network Update Rate
|
||||
tags: article gamedev network multiplayer
|
||||
created: 2024-02-21T22:17:01Z
|
||||
published: true
|
||||
---
|
||||
|
||||
In game development, the send rate of server and client updates dictates how good the prediction of the real player position is. However, not all increases in send rate give you equal returns for the increased CPU and bandwidth costs you're paying.
|
||||
|
||||
I've recorded and averaged the prediction error over multiple runs on some common send rates to see how much the prediction improves. The test involves a character running in circles which tests a range of parameters like extrapolation, interpolation and update rate.
|
||||
|
||||
## Data
|
||||
|
||||
| Update rate | Error distance |
|
||||
| ----------------------------------- | -------------- |
|
||||
| 1 Hz | 73.4 cm |
|
||||
| 2 Hz | 51.5 cm |
|
||||
| 5 Hz | 23.3 cm |
|
||||
| 10 Hz | 12.5 cm |
|
||||
| 15 Hz | 8.9 cm |
|
||||
| 20 Hz | 7.4 cm |
|
||||
| 25 Hz | 6.2 cm |
|
||||
| 50 Hz | 4.5 cm |
|
||||
| 100 Hz | 3.5 cm |
|
||||
|
||||
The error distance represents the distance of the client's predicted position to the server position sampled on each network packet receival.
|
||||
The move speed used in the test was 4.5 meters per second.
|
||||
|
||||
The test data shows that there's barely any noticeable benefit in running more than 20 Hz. The movement actually looks very smooth with only 10 Hz, but suffers in a few edge cases (ping-pong movement) where 20 Hz can lead to much better results. The final choice will of course depend on your prediction algorithms and the game type.
|
||||
|
||||
## Conclusion
|
||||
|
||||
- Too little updates make it really hard to predict where the player is
|
||||
- Too many updates will severely increase server CPU & bandwidth usage
|
||||
- Prefer a send & receive rate somewhere in the ballpark of 10-20 Hz
|
21
posts/projects.md
Normal file
21
posts/projects.md
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
title: Projects
|
||||
tags: about
|
||||
created: 2024-03-03T23:11:14Z
|
||||
published: true
|
||||
---
|
||||
|
||||
| | |
|
||||
| --- | --- |
|
||||
| 👤 [akyoto.dev](https://git.akyoto.dev/web/akyoto.dev) | this website |
|
||||
| 🚦 [assert](https://git.akyoto.dev/go/assert) | assertions for software correctness |
|
||||
| 🎮 [bom](https://git.akyoto.dev/game/bom) | game using Godot with Go servers |
|
||||
| 🌈 [color](https://git.akyoto.dev/go/color) | color for terminals |
|
||||
| 💿 [data](https://git.akyoto.dev/go/data) | in-memory key value store |
|
||||
| 🔢 [hash](https://git.akyoto.dev/go/hash) | non-cryptographic hash for etags |
|
||||
| 🏠 [home](https://git.akyoto.dev/sys/home) | personal dotfiles |
|
||||
| 📃 [markdown](https://git.akyoto.dev/go/markdown) | markdown renderer for this site |
|
||||
| 💃 [notify.moe](https://git.akyoto.dev/web/notify.moe) | anime tracker |
|
||||
| 🌱 [q](https://git.akyoto.dev/cli/q) | simple programming language |
|
||||
| 🔗 [router](https://git.akyoto.dev/go/router) | http router based on radix trees |
|
||||
| 🚀 [web](https://git.akyoto.dev/go/web) | web server for this site |
|
38
posts/separation-of-concerns.md
Normal file
38
posts/separation-of-concerns.md
Normal file
@ -0,0 +1,38 @@
|
||||
---
|
||||
title: Separation of Concerns
|
||||
tags: article software
|
||||
created: 2018-06-25T00:16:40Z
|
||||
published: true
|
||||
---
|
||||
|
||||
> Do one thing and do it well.
|
||||
|
||||
Separation of concerns is a concept that can be described as the art of only doing what the tool should be concerned with, never more than needs to be done.
|
||||
You don't expect a knife to function as a lighter. A knife only needs to cut and it does that one thing very well.
|
||||
|
||||
## How does this apply to software?
|
||||
|
||||
Any type of modern software architecture allows us to structure our code into modules, packages, files, libraries, engines and other types of abstractions that let us reuse existing functionality.
|
||||
|
||||
Whenever you write a new module, ask yourself this: Does this module really need to contain all the code it has inside it?
|
||||
Is every part of this module really dealing with the same problem, the same _concern_?
|
||||
|
||||
Chances are high that you have written a module in the past that does much more than it needs to do. The module is doing things that it should not be concerned with. These different concerns should be separated into different modules, each dealing only with the bare minimum they need to deal with.
|
||||
|
||||
## What's the benefit?
|
||||
|
||||
By doing so, you will notice that your code gains a higher chance of being reused in a different project.
|
||||
Modules can be tested much easier because the amount of possible states you need to reason about becomes much smaller, therefore contributing to overall better software quality.
|
||||
|
||||
## What about frameworks?
|
||||
|
||||
This is actually one of the main reasons I don't like monolithic frameworks. You import one package (the framework) and it concerns itself with a multitude of problems. It becomes hard to test and reason about this software. Understanding and fixing bugs becomes difficult because it deals with so many things, the faulty state could have been caused by any part of the system.
|
||||
|
||||
Instead, what software developers should strive for, is to make small and independent packages that deal with a single problem only.
|
||||
Do not offer a single package that deals with all of the world's problems, offer a set of modules that developers can choose from. By doing so, a framework becomes a _specification_ of modules, a _concept_, rather than an actual package with thousand dependencies.
|
||||
|
||||
## Conclusion
|
||||
|
||||
- Separate concerns.
|
||||
- Do one thing and do it well.
|
||||
- Avoid centralism, prefer composition.
|
96
posts/static-ip-configuration.md
Normal file
96
posts/static-ip-configuration.md
Normal file
@ -0,0 +1,96 @@
|
||||
---
|
||||
title: Static IP Configuration
|
||||
tags: article guide software linux config
|
||||
created: 2023-07-02T21:43:42Z
|
||||
published: true
|
||||
---
|
||||
|
||||
When installing Arch Linux on a VPS, you usually need to enable VNC and configure the networking by yourself before you can SSH into your machine.
|
||||
This post serves as a guide to connect your server to the internet.
|
||||
|
||||
## Connectivity check
|
||||
|
||||
First of all, confirm that networking hasn't been set up yet:
|
||||
|
||||
```bash
|
||||
ping google.com
|
||||
```
|
||||
|
||||
There should be no response from the ping.
|
||||
|
||||
## Network interface
|
||||
|
||||
Now let's find out the name of the network interface:
|
||||
|
||||
```bash
|
||||
networkctl list
|
||||
```
|
||||
|
||||
You should see an ethernet device named like `eth0` or `ens0`.
|
||||
The number at the end will differ.
|
||||
This is the name of the network interface we'll configure in the following steps.
|
||||
|
||||
## Network manager
|
||||
|
||||
Arch Linux uses `systemd` which includes the `systemd-networkd` network manager by default.
|
||||
To configure the network manager, edit the following file:
|
||||
|
||||
```bash
|
||||
sudo vim /etc/systemd/network/20-wired.network
|
||||
```
|
||||
|
||||
Start by specifying the network interface name:
|
||||
|
||||
```ini
|
||||
[Match]
|
||||
Name=ens0
|
||||
```
|
||||
|
||||
Now you need to enter your IP address, subnet mask and the gateway.
|
||||
Ideally your VPS provider should include that data in your dashboard.
|
||||
You can enter the addresses in the network section:
|
||||
|
||||
```ini
|
||||
[Network]
|
||||
Address=2.1.1.2/32
|
||||
Gateway=2.1.1.1
|
||||
DNS=1.1.1.1
|
||||
```
|
||||
|
||||
The DNS shown here is the Cloudflare DNS because it's reliable and easy to remember,
|
||||
but feel free to use a different DNS.
|
||||
|
||||
If your VPS has an IPv6 address you can add more of the same lines to the network section:
|
||||
|
||||
```ini
|
||||
Address=1111:1111:1111::2345
|
||||
Gateway=1111:1111:1111::1111
|
||||
DNS=2606:4700:4700::1111
|
||||
```
|
||||
|
||||
## Service installation
|
||||
|
||||
Try to start the DNS resolver and the network manager:
|
||||
|
||||
```bash
|
||||
sudo systemctl start systemd-resolved
|
||||
sudo systemctl start systemd-networkd
|
||||
```
|
||||
|
||||
Check if we're online on both IPv4 and IPv6:
|
||||
|
||||
```bash
|
||||
ping -4 google.com
|
||||
ping -6 google.com
|
||||
```
|
||||
|
||||
If everything went smooth, make the DNS resolver and the network manager start automatically on boot:
|
||||
|
||||
```bash
|
||||
sudo systemctl enable systemd-resolved
|
||||
sudo systemctl enable systemd-networkd
|
||||
```
|
||||
|
||||
## DNS records
|
||||
|
||||
To finalize the setup, add an `A` record for IPv4 and an `AAAA` record for IPv6 to your DNS and your server should be fully configured.
|
65
posts/tabs-vs-spaces.md
Normal file
65
posts/tabs-vs-spaces.md
Normal file
@ -0,0 +1,65 @@
|
||||
---
|
||||
title: Tabs vs. Spaces
|
||||
tags: article software
|
||||
created: 2018-06-24T07:03:20Z
|
||||
published: true
|
||||
---
|
||||
|
||||
Let's take a look at tabs vs. spaces. This discussion is famous amongst all skill levels of developers and it seems that noone can really convince the other side. People have different opinions on why one side is better than the other and cannot unanimously agree on one style. I think one of the main reasons for this problem is that the discussion is always about 100% tabs vs. 100% spaces instead of looking at an alternative approach: Using both.
|
||||
|
||||
## Both?
|
||||
|
||||
At first it might sound weird, but we're not just going to randomly insert a different indentation character whenever we want to. There is going to be a ruleset and that ruleset will be well defined. That means by going hybrid, there are no discussions needed for when tabs or spaces should be used, as every situation will be clearly covered by those rules.
|
||||
|
||||
## Which rules, exactly?
|
||||
|
||||
1. Tabs for semantic indentation
|
||||
2. Spaces for presentational alignment
|
||||
|
||||
### Semantic indentation
|
||||
|
||||
> Relating to meaning in language or logic.
|
||||
|
||||
Semantic indentation is required when the whitespace has a meaning in the given context. Note that _meaning_ doesn't necessarily imply that it is needed for the compiler to process the code as in the case of Python e.g., _meaning_ can simply refer to an indented block of code, let's say a `for` loop, where the body of the loop is semantically different from the surrounding code and therefore is indented.
|
||||
|
||||
```go
|
||||
for {
|
||||
// This is semantic indentation: Use tabs.
|
||||
}
|
||||
```
|
||||
|
||||
Semantic indentation should be performed as the very first step, using **tabs**.
|
||||
|
||||
Spaces would need to be repeated multiple times to represent a single meaning. This is inefficient for disk space and therefore code parsing, let alone the fact that doing so doesn't give us any benefits in the first place. Or when was the last time you used 4 square brackets to represent an array in JSON?
|
||||
|
||||
```json
|
||||
[[[["Behold the new JSON standard!"]]]]
|
||||
```
|
||||
|
||||
Why use more when you can do it with less?
|
||||
|
||||
We also get the added benefit of letting every team member be able to adjust the tab width and therefore personalize how much pixel space he or she would prefer to be used for a single tab. This can heavily improve readability for a person that is used to very different widths on his or her monitor. Normally, if you used tabs 100% of the time, this argument would be held up **against** using tabs because it can break the alignment of your code. That is exactly why we are going to define separate rules for alignment.
|
||||
|
||||
### Presentational alignment
|
||||
|
||||
> The proper positioning or state of adjustment of parts in relation to each other.
|
||||
|
||||
Presentational alignment refers to positioning different parts better in relation to each other and does not convey any meaning. It will make the code look better but the whitespace doesn't convey that the code is different from the surrounding code, thus being only used to align parts with each other for presentational purposes.
|
||||
|
||||
```go
|
||||
type User struct {
|
||||
// Note how we're using tabs to indent the struct
|
||||
// and spaces to align the name of the data type.
|
||||
ID string
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
```
|
||||
|
||||
Alignment should be used _after_ semantic indentation and should be done using **spaces**. The reason is that alignment requires precise positioning on a monospace font and the tab character can't do that because it has dynamic width.
|
||||
|
||||
Your team members will thank you because they can finally change the indentation width to something they like and it won't break the alignment of the code.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Rather than going full tabs or full spaces, consider using a hybrid approach where each character does what it's best at and therefore combining the best of both worlds. I have been using this system for as long as I can remember and I never had a problem with it. For me, this is the de facto standard nowadays and the more people start switching to this system, the better. Amen.
|
98
posts/the-final-newline.md
Normal file
98
posts/the-final-newline.md
Normal file
@ -0,0 +1,98 @@
|
||||
---
|
||||
title: The Final Newline
|
||||
tags: article software
|
||||
created: 2023-06-28T14:52:08Z
|
||||
published: true
|
||||
---
|
||||
|
||||
The beautiful thing about the language of mathematics is that it is precise and definitions must apply to every case
|
||||
presented, 100%, else the definition is considered wrong and needs to be altered. 99% isn't good enough for a
|
||||
definition. If there is even a single case showing that the proposed definition doesn't apply to it, we consider the definition to be incorrect.
|
||||
|
||||
I'm a little surprised that in software engineering, which must be as precise as mathematics in definitions and specifications, we have come to accept this definition of a line:
|
||||
|
||||
> A sequence of zero or more non-newline characters plus a terminating newline character.
|
||||
|
||||
As mentioned earlier, if there is even one case that doesn't satisfy the definition, then it's incorrect.
|
||||
This definition of a line completely falls apart when you look at a file with the following contents:
|
||||
|
||||
```text
|
||||
This is a line of text.
|
||||
```
|
||||
|
||||
Let's save this as `single-line.txt`, without the newline character at the end.
|
||||
|
||||
Is this a line? Of course it is, humans don't use line terminators when writing something down in real life.
|
||||
Ask any person around you, they will say "yeah, it's one line of text".
|
||||
|
||||
## Counting lines
|
||||
|
||||
Without a final newline character, POSIX compliant programs will fail to recognize this line:
|
||||
|
||||
```bash
|
||||
> "This is a line of text." | wc -l
|
||||
0
|
||||
```
|
||||
|
||||
That's because the POSIX definition of a line is wrong. We have one line of text and the computer is telling us we have zero lines in our file.
|
||||
|
||||
Now, before you go and send pull requests to the authors of `wc`, let me quickly add: Their documentation explicitly mentions that the flag abbreviated with `-l` does - in fact - not count lines. 🤔
|
||||
|
||||
It only counts _newlines_. The authors of `wc`, Paul Rubin and David MacKenzie, probably wanted to avoid this
|
||||
controversy and the documentation states that it only counts newline characters, not lines.
|
||||
|
||||
## Separator vs. Terminator
|
||||
|
||||
There are 2 ways to interpret the newline character:
|
||||
|
||||
- As a line separator
|
||||
- As a line terminator
|
||||
|
||||
With the "line separator" interpretation you basically treat the contents of a file as a
|
||||
|
||||
```go
|
||||
strings.Join(lines, "\n")
|
||||
```
|
||||
|
||||
whereas the "line terminator" interpretation is better expressed as:
|
||||
|
||||
```go
|
||||
for _, line := range lines {
|
||||
w.WriteString(line)
|
||||
w.WriteByte('\n')
|
||||
}
|
||||
```
|
||||
|
||||
## Definition vs. Regulation
|
||||
|
||||
As we have seen, not every line is terminated with a newline character.
|
||||
Thus we cannot regard the "line terminator" interpretation as a _definition_, because it's incorrect.
|
||||
It's a _regulation_.
|
||||
|
||||
A regulation is different because it forces you to do something as opposed to trying to define something.
|
||||
|
||||
## Does the POSIX regulation make sense?
|
||||
|
||||
The thing about regulations is that we all hate them, but we hate inconsistencies even more.
|
||||
|
||||
Even if I personally think that the newline character should be a line separator because it's more in line (pun intended) with how humans see a text file,
|
||||
I also recognize that some fights aren't worth fighting.
|
||||
|
||||
It would have been a lot easier to discuss this topic in the early days of computing, before the existence of tools that adopted the POSIX standard.
|
||||
Now it's hard to argue against it when your co-worker just wants an easy way to disable the annoying "No newline at end of file" warning on every `git diff`.
|
||||
|
||||
So my suggestion to developers is that everybody needs to decide for themselves what is worth their time.
|
||||
|
||||
If you say that you want to argue with existing standards and try to improve them for the future, then I will fully
|
||||
respect that. But know what you're getting yourself into. Everything has an [opportunity cost](https://en.wikipedia.org/wiki/Opportunity_cost).
|
||||
I think it's mostly the younger generation that tries to initiate changes and that's not necessarily a bad thing.
|
||||
|
||||
Deep down, I really wish somebody would stop this line terminating madness someday. But this isn't my fight.
|
||||
|
||||
## Conclusion
|
||||
|
||||
- In an ideal world, newline characters would be line separators, not line terminators
|
||||
- Tools in the UNIX world have already widely adopted the POSIX regulation
|
||||
- It's very hard to make a change in this area
|
||||
- Decide for yourself if this is a fight that is worth your time [or not](https://en.wikipedia.org/wiki/Law_of_triviality)
|
||||
- If you are not the owner, adopt the style regulations of the project you are contributing to
|
102
posts/vector.md
Normal file
102
posts/vector.md
Normal file
@ -0,0 +1,102 @@
|
||||
---
|
||||
title: Vector
|
||||
tags: note vector math
|
||||
created: 2023-08-29T11:59:28Z
|
||||
published: true
|
||||
---
|
||||
|
||||
## 2D
|
||||
|
||||
### Angle
|
||||
|
||||
```go
|
||||
atan2(y, x)
|
||||
```
|
||||
|
||||
### Cross
|
||||
|
||||
```go
|
||||
x * y2 - y * x2
|
||||
```
|
||||
|
||||
### Distance
|
||||
|
||||
```go
|
||||
(a - b).Length()
|
||||
```
|
||||
|
||||
### Dot
|
||||
|
||||
```go
|
||||
x * x2 + y * y2
|
||||
```
|
||||
|
||||
### From angle
|
||||
|
||||
```go
|
||||
x = cos(angle)
|
||||
y = sin(angle)
|
||||
```
|
||||
|
||||
### Length
|
||||
|
||||
```go
|
||||
sqrt(x * x + y * y)
|
||||
```
|
||||
|
||||
### Normalize
|
||||
|
||||
```go
|
||||
length := x * x + y * y
|
||||
|
||||
if length == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
length = sqrt(length)
|
||||
x /= length
|
||||
y /= length
|
||||
```
|
||||
|
||||
## 3D
|
||||
|
||||
### Cross
|
||||
|
||||
```go
|
||||
( y * z2 - z * y2,
|
||||
z * x2 - x * z2,
|
||||
x * y2 - y * x2 )
|
||||
```
|
||||
|
||||
### Distance
|
||||
|
||||
```go
|
||||
(a - b).Length()
|
||||
```
|
||||
|
||||
### Dot
|
||||
|
||||
```go
|
||||
x * x2 + y * y2 + z * z2
|
||||
```
|
||||
|
||||
### Length
|
||||
|
||||
```go
|
||||
sqrt(x * x + y * y + z * z)
|
||||
```
|
||||
|
||||
### Normalize
|
||||
|
||||
```go
|
||||
length := x * x + y * y + z * z
|
||||
|
||||
if length == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
length = sqrt(length)
|
||||
x /= length
|
||||
y /= length
|
||||
z /= length
|
||||
```
|
46
posts/workspace-matrix.md
Normal file
46
posts/workspace-matrix.md
Normal file
@ -0,0 +1,46 @@
|
||||
---
|
||||
title: Workspace Matrix
|
||||
tags: article software linux productivity
|
||||
created: 2024-03-01T21:57:39Z
|
||||
published: true
|
||||
---
|
||||
|
||||
I use a 3 x 3 workspace matrix with the numbers on my numpad bound to each workspace.
|
||||
My wife also started using the same workflow and she has been pretty fond of it ever since.
|
||||
|
||||
I wanted to share this method in case anyone else wants to try this on GNOME or Hyprland.
|
||||
For KDE users: I believe the settings menu lets you configure this out of the box.
|
||||
|
||||
## Gnome
|
||||
|
||||
Normally, you can only set 4 workspace shortcuts in the GNOME settings menu.
|
||||
However, by accessing the dconf settings you can circumvent this restriction.
|
||||
|
||||
```bash
|
||||
export WS=/org/gnome/desktop/wm/keybindings/switch-to-workspace
|
||||
dconf write $WS-1 "['KP_7']"
|
||||
dconf write $WS-2 "['KP_8']"
|
||||
dconf write $WS-3 "['KP_9']"
|
||||
dconf write $WS-4 "['KP_4']"
|
||||
dconf write $WS-5 "['KP_5']"
|
||||
dconf write $WS-6 "['KP_6']"
|
||||
dconf write $WS-7 "['KP_1']"
|
||||
dconf write $WS-8 "['KP_2']"
|
||||
dconf write $WS-9 "['KP_3']"
|
||||
```
|
||||
|
||||
## Hyprland
|
||||
|
||||
Simply add this to your config file.
|
||||
|
||||
```ini
|
||||
bind = , KP_Home, workspace, 1
|
||||
bind = , KP_Up, workspace, 2
|
||||
bind = , KP_Prior, workspace, 3
|
||||
bind = , KP_Left, workspace, 4
|
||||
bind = , KP_Begin, workspace, 5
|
||||
bind = , KP_Right, workspace, 6
|
||||
bind = , KP_End, workspace, 7
|
||||
bind = , KP_Down, workspace, 8
|
||||
bind = , KP_Next, workspace, 9
|
||||
```
|
@ -100,6 +100,7 @@ blockquote p::after {
|
||||
h1 {
|
||||
color: white;
|
||||
font-size: 2.2rem;
|
||||
font-weight: normal;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
@ -137,28 +138,14 @@ th:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 800px) {
|
||||
h2 {
|
||||
margin-left: -1rem;
|
||||
}
|
||||
|
||||
.post-header {
|
||||
margin-left: -1rem;
|
||||
}
|
||||
|
||||
.post-time {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
header,
|
||||
body > header,
|
||||
main {
|
||||
width: 100%;
|
||||
max-width: var(--max-width);
|
||||
padding: var(--padding);
|
||||
}
|
||||
|
||||
header {
|
||||
body > header {
|
||||
max-width: calc(var(--max-width) + 4rem);
|
||||
}
|
||||
|
||||
@ -186,11 +173,21 @@ nav a:hover {
|
||||
color: var(--main-color);
|
||||
}
|
||||
|
||||
p time {
|
||||
article header time {
|
||||
font-size: 0.8rem;
|
||||
color: var(--grey-color);
|
||||
}
|
||||
|
||||
.blog li {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.blog li time {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
color: var(--grey-color);
|
||||
}
|
||||
|
||||
.comment {
|
||||
color: gray;
|
||||
font-style: italic;
|
||||
@ -238,11 +235,12 @@ p time {
|
||||
}
|
||||
|
||||
@media (min-width: 800px) {
|
||||
article header,
|
||||
h2 {
|
||||
margin-left: -1rem;
|
||||
}
|
||||
|
||||
p time {
|
||||
margin-left: .5rem;
|
||||
article header time {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user