beat's Avatar

beat

@beathagenlocher.com

Mostly random stuff I find interesting, or other sharable thoughts that cross my mind. https://beathagenlocher.com

63
Followers
281
Following
262
Posts
17.07.2024
Joined
Posts Following

Latest posts by beat @beathagenlocher.com

Originally posted at https://beathagenlocher.com/stream#00129

06.03.2026 12:08 πŸ‘ 0 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0
Some time ago, I visited a friend who's fairly well versed in tech (e.g. NeoVim, can code) and watches him struggle with editing a file in Word fast on someone else's Laptop.

I asked him whether he knew about <KB>Ctrl-arrow</KB> for wordwise movement (which he didn't), and was fairly surprised. But in hindsight, I didn't know that for the longest time either.

I took this as an example to start writing on Better Computer Use (then only a collection of basically "Keyboard Shortcuts You Might Want To Know").

Today I revisited this, and something that started out as "You might want to know these shortcuts" ended in a discussion on how Software Developers and Product Designers should structure their UIs.

Feel free to check it out, tell me what you think, and tell me where I'm wrong :)

(Or just wait until it's more polished.)

:)

Some time ago, I visited a friend who's fairly well versed in tech (e.g. NeoVim, can code) and watches him struggle with editing a file in Word fast on someone else's Laptop. I asked him whether he knew about <KB>Ctrl-arrow</KB> for wordwise movement (which he didn't), and was fairly surprised. But in hindsight, I didn't know that for the longest time either. I took this as an example to start writing on Better Computer Use (then only a collection of basically "Keyboard Shortcuts You Might Want To Know"). Today I revisited this, and something that started out as "You might want to know these shortcuts" ended in a discussion on how Software Developers and Product Designers should structure their UIs. Feel free to check it out, tell me what you think, and tell me where I'm wrong :) (Or just wait until it's more polished.) :)

Hey :) I've greatly expanded upon an older note of mine. Check it out!

#Simplicity #Writing

06.03.2026 12:08 πŸ‘ 0 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0

Read the full post at https://beathagenlocher.com/stream#00128

04.03.2026 23:49 πŸ‘ 0 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0
Today I finally tackled something that has annoyed me for a while since switching most of my editing/digital gardening/second brain-writing over to Emacs.

Whenever I took 30 minutes and tried to get title fetching of links to work, I couldn't get it to work in this self-allotted time. So I asked Opus to do the dumbest thing possible: Just fetch it via curl, and then apply stuff asynchronously.

(Obviously the other, non-dumb way should work too, but I've spent way to much time on this now either way.)  
(And apparently there's also `url-insert-file-contents`, maybe I'gonna try that some other time.)

```elisp
(defun flt--url-at-point ()
  "Get URL at point, or nil."
  (thing-at-point 'url))

(defun flt--url-from-kill-ring ()
  "Get URL from kill ring if it looks like one, or nil."
  (let ((text (current-kill 0 t)))
    (when (and text (string-match-p "\\`https?://" text))
      (string-trim text))))

(defun flt--get-url ()
  "Get URL from point first, then kill ring. Nil if neither has one."
  (or (flt--url-at-point)
      (flt--url-from-kill-ring)))

(defun flt--extract-title (html)
  "Extract <title> content from HTML string."
  (when (string-match "<title[^>]*>\\([^<]*\\)</title>" html)
    (string-trim (match-string 1 html))))

(defun flt--insert-markdown-link (url title marker)
  "Replace URL at MARKER with a markdown link, or insert at MARKER."
  (with-current-buffer (marker-buffer marker)
    (save-excursion
      (goto-char marker)
      ;; If there's a raw URL at point, replace it
      (let ((url-bounds (thing-at-point-bounds-of-url-at-point)))
        (if url-bounds
            (progn
              (delete-region (car url-bounds) (cdr url-bounds))
              (insert (format "%s" title url)))
          (insert (format "%s" title url)))))))

(defun flt-fetch-link-title ()
  "Fetch the title of a URL (at point or kill ring) and insert a markdown link.
If point is on a URL, replaces it. Otherwise inserts at point."
  (interactive)
  (let ((url (flt--get-url)))
    (if (not url)
        (message "No URL found at point or in kill ring.")
      (let ((marker (point-marker))
            (buf (generate-new-buffer " *flt-curl*")))
        (message "Fetching title for %s..." url)
        (make-process
         :name "flt-curl"
         :buffer buf
         :command (list "curl" "-sL" "-m" "10"
                        "-H" "User-Agent: Emacs"
                        "--" url)
         :sentinel
         (lambda (proc _event)
           (when (eq (process-status proc) 'exit)
             (let ((title
                    (with-current-buffer (process-buffer proc)
                      (flt--extract-title (buffer-string)))))
               (if title
                   (progn
                     (flt--insert-markdown-link url title marker)
                     (message "Inserted: %s" title url))
                 (message "Could not extract title from %s" url)))
             (kill-buffer (process-buffer proc))
             (set-marker marker nil))))))))

(map! :leader
      :desc "->title" :nv "d f" #'flt-fetch-link-title)
```

And in the meantime, I also learned that _markers_ exist!

So now, I can fetch link titles again, like I'm used to :)

Today I finally tackled something that has annoyed me for a while since switching most of my editing/digital gardening/second brain-writing over to Emacs. Whenever I took 30 minutes and tried to get title fetching of links to work, I couldn't get it to work in this self-allotted time. So I asked Opus to do the dumbest thing possible: Just fetch it via curl, and then apply stuff asynchronously. (Obviously the other, non-dumb way should work too, but I've spent way to much time on this now either way.) (And apparently there's also `url-insert-file-contents`, maybe I'gonna try that some other time.) ```elisp (defun flt--url-at-point () "Get URL at point, or nil." (thing-at-point 'url)) (defun flt--url-from-kill-ring () "Get URL from kill ring if it looks like one, or nil." (let ((text (current-kill 0 t))) (when (and text (string-match-p "\\`https?://" text)) (string-trim text)))) (defun flt--get-url () "Get URL from point first, then kill ring. Nil if neither has one." (or (flt--url-at-point) (flt--url-from-kill-ring))) (defun flt--extract-title (html) "Extract <title> content from HTML string." (when (string-match "<title[^>]*>\\([^<]*\\)</title>" html) (string-trim (match-string 1 html)))) (defun flt--insert-markdown-link (url title marker) "Replace URL at MARKER with a markdown link, or insert at MARKER." (with-current-buffer (marker-buffer marker) (save-excursion (goto-char marker) ;; If there's a raw URL at point, replace it (let ((url-bounds (thing-at-point-bounds-of-url-at-point))) (if url-bounds (progn (delete-region (car url-bounds) (cdr url-bounds)) (insert (format "%s" title url))) (insert (format "%s" title url))))))) (defun flt-fetch-link-title () "Fetch the title of a URL (at point or kill ring) and insert a markdown link. If point is on a URL, replaces it. Otherwise inserts at point." (interactive) (let ((url (flt--get-url))) (if (not url) (message "No URL found at point or in kill ring.") (let ((marker (point-marker)) (buf (generate-new-buffer " *flt-curl*"))) (message "Fetching title for %s..." url) (make-process :name "flt-curl" :buffer buf :command (list "curl" "-sL" "-m" "10" "-H" "User-Agent: Emacs" "--" url) :sentinel (lambda (proc _event) (when (eq (process-status proc) 'exit) (let ((title (with-current-buffer (process-buffer proc) (flt--extract-title (buffer-string))))) (if title (progn (flt--insert-markdown-link url title marker) (message "Inserted: %s" title url)) (message "Could not extract title from %s" url))) (kill-buffer (process-buffer proc)) (set-marker marker nil)))))))) (map! :leader :desc "->title" :nv "d f" #'flt-fetch-link-title) ``` And in the meantime, I also learned that _markers_ exist! So now, I can fetch link titles again, like I'm used to :)

Async Link Fetching in Emacs

#TIL #Emacs #DoomEmacs

04.03.2026 23:49 πŸ‘ 0 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0

Originally posted at https://beathagenlocher.com/stream#00127

03.03.2026 16:42 πŸ‘ 0 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0
I've been dragging quite a few notes around with me for the last one, two weeks.

Here they are! :)

I've been dragging quite a few notes around with me for the last one, two weeks. Here they are! :)

Just uploaded a bunch of my already-written/accumulated notes :)

#Writing

03.03.2026 16:42 πŸ‘ 1 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0

Read the full post at https://beathagenlocher.com/stream#00126

02.03.2026 23:47 πŸ‘ 0 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0
For presentations, I'm using slidev, a pretty cool and versatile tool (Markdown slides with inline HTML/Vue, so you can do basically anything on your slides), which has lots of cool features.

One minor annoyance: If you're using either of the QRCode addons and want to add more than one QR Code per slide deck, it breaks.

Luckily, with the power of the web, this is pretty easy to fix:

We can just define a component:

```vue
<template>
  <QrcodeCanvas
    :value="value"
    :size="width"
    :foreground="foreground"
    :background="background"
    :margin="margin"
    level="H"
  />
</template>

<script lang="ts" setup>
import { QrcodeCanvas } from 'qrcode.vue'

const props = withDefaults(
  defineProps<{
    value: string
    width?: number
    height?: number
    color?: string
    margin?: number
  }>(),
  {
    width: 200,
    color: '#000000',
    margin: 2,
  },
)

const foreground = props.color.startsWith('#') ? props.color : `#${props.color}`
const background = '#FFFFFF'
</script>
```

And use it:

```vue
<QRCode class="rounded" value="https://beathagenlocher.com/me" :width="140" :height="140" />
```

Sweet!

For presentations, I'm using slidev, a pretty cool and versatile tool (Markdown slides with inline HTML/Vue, so you can do basically anything on your slides), which has lots of cool features. One minor annoyance: If you're using either of the QRCode addons and want to add more than one QR Code per slide deck, it breaks. Luckily, with the power of the web, this is pretty easy to fix: We can just define a component: ```vue <template> <QrcodeCanvas :value="value" :size="width" :foreground="foreground" :background="background" :margin="margin" level="H" /> </template> <script lang="ts" setup> import { QrcodeCanvas } from 'qrcode.vue' const props = withDefaults( defineProps<{ value: string width?: number height?: number color?: string margin?: number }>(), { width: 200, color: '#000000', margin: 2, }, ) const foreground = props.color.startsWith('#') ? props.color : `#${props.color}` const background = '#FFFFFF' </script> ``` And use it: ```vue <QRCode class="rounded" value="https://beathagenlocher.com/me" :width="140" :height="140" /> ``` Sweet!

Finally fixed a minor slide annoyance of mine: Non-working QR codes

#Slidev #QRCode #slides #Vue

02.03.2026 23:47 πŸ‘ 0 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0
Beat Hagenlochers Digital Garden Beat's digital garden with notes and essays on Software Engineering, and other written thinking

Totally fair ^^
In case it helps, I've got some notes on Nix stuff: beathagenlocher.com?q=nix
(but there's plenty of good stuff online too nowadays)

And feel free to ask questions!

02.03.2026 22:13 πŸ‘ 0 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0

Then, when you're managing all of your projects written in, say, python inside nix/flakes, you can remove the global python from your homebrew state.

And concurrently: As soon as you've got a bit of a grasp on flakes, you can start a managed /home/user via home-manager, and so on.

02.03.2026 17:11 πŸ‘ 2 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0

The cool thing is though that you don't even need to switch all-in-one, you can make the transition pretty gradual.
You can start out with just one project, and manage those project's dependencies in a flakes' devShell.

02.03.2026 17:11 πŸ‘ 1 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0
"Simple Made Easy" - Rich Hickey (2011)
"Simple Made Easy" - Rich Hickey (2011) YouTube video by Strange Loop Conference

[Simple](www.youtube.com/watch?v=SxdO...) yes, not easy, very much worth it.

02.03.2026 17:06 πŸ‘ 1 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0
Preview
GitHub - doomemacs/doomemacs: An Emacs framework for the stubborn martian hacker An Emacs framework for the stubborn martian hacker - doomemacs/doomemacs

May I suggest doom?

github.com/doomemacs/do...

01.03.2026 03:07 πŸ‘ 1 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0
Preview
GitHub - emacs-evil/evil: The extensible vi layer for Emacs. The extensible vi layer for Emacs. Contribute to emacs-evil/evil development by creating an account on GitHub.

Over here, it’s called the _evil_ side.

😁

github.com/emacs-evil/e...

01.03.2026 02:58 πŸ‘ 1 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0

Yep, very much so. Thanks for writing this up!

28.02.2026 09:33 πŸ‘ 1 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0

Fish abbreviation from 'co' to 'claude --model opus' :)

27.02.2026 14:21 πŸ‘ 2 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0

Originally posted at https://beathagenlocher.com/stream#00125

27.02.2026 10:02 πŸ‘ 0 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0
Today I learned that there's magic `pre<*>` and `post<*>` hooks inside `npm` (and everything npm-compatible):

-> https://docs.npmjs.com/cli/v8/using-npm/scripts

I think I would've preferred less magic-side-effect-ness, but fair, I guess.

So, to run something post `install`

```json
{
  "scripts": {
    // ...,
    "postinstall": "echo 'hello postinstall'",
  }
}
```

Caveat:

For `npm`, this only works when literally running `npm install` (or `npm i`), but now when running `npm i <some-package>`.  
Well, that's what you get, I guess?  
I'm even less convinced by its usefulness now.

For `bun` at least, both seem to work :)

Today I learned that there's magic `pre<*>` and `post<*>` hooks inside `npm` (and everything npm-compatible): -> https://docs.npmjs.com/cli/v8/using-npm/scripts I think I would've preferred less magic-side-effect-ness, but fair, I guess. So, to run something post `install` ```json { "scripts": { // ..., "postinstall": "echo 'hello postinstall'", } } ``` Caveat: For `npm`, this only works when literally running `npm install` (or `npm i`), but now when running `npm i <some-package>`. Well, that's what you get, I guess? I'm even less convinced by its usefulness now. For `bun` at least, both seem to work :)

Learned something about package.json postinstall scripts :)

#npm #JavaScript #TypeScript #bun #pnpm #TIL

27.02.2026 10:02 πŸ‘ 2 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0

Read the full post at https://beathagenlocher.com/nix-flake

20.02.2026 16:38 πŸ‘ 0 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0
An entry point for Nix stuff.

# Writing your first flake

Your flake is the entry point to your application built and run with nix.

Your flake is a function from `inputs` (which consist of urls for getting package sets) to `outputs`, with outputs being one big _attribute set_.

This `outputs` _attribute set_ looks something like this (very much like a JSON object):

```nix
# outputs
{
  apps = { ... };
  checks = { ... };
  devShells = { ... };
  formatter = { ... };
  legacyPackages = { ... };
  nixosConfigurations = { ... };
  nixosModules = { ... };
  overlays = { ... };
  packages = { ... };
}
```

Every key in here is a special, magic key that Nix interprets in a certain way.

For the minimal (non-functional) flake:

```nix
{
  inputs = {};

  outputs = {...}: {};
}
```

all of them are empty.

Through populating them, we give our Nix Flake additional capabilities.

The first capability to give our Nix Flakes is normally a _development shell_.

In Nix Language, we create this like this:

```nix title="flake.nix"
{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
  };

  outputs = { nixpkgs, ... }: {
    devShells.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.mkShell {};
  };
}
```

And run it via `nix develop` (getting it? The `devShells` output gets run by `nix develop`):

```shell
> nix develop
(nix-shell-env)>
```

But that doesn't do anything yet.

For it to do something we need to add something into the devshell:

```nix title="flake.nix" del={7} ins={8-10}
{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
  };

  outputs = { nixpkgs, ... }: {
    devShells.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.mkShell {};
    devShells.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.mkShell {
        packages = [ nixpkgs.legacyPackages.x86_64-linux.hello ];
    };
  };
}
```

(Run `nix develop`, and then we have the `hello` executable available.):

```shell
> nix develop
(nix-shell-env)> hello
Hello, world!
```

Let's clean this up a bit, and then we're done for now.  
There's lots of repetition in the current file, and the solution to this is not as easy as inserting a `let` block.

The accepted and easily extensible solution for this is flake-parts, but their homepage is pretty hard to understand at first.

We want to translate our example to this:

```nix title="flake.nix"
{
  inputs = {
    nixpkgs.url = "https://github.com/nixos/nixpkgs?ref=nixos-unstable";
  };

  outputs = inputs@{ flake-parts, ... }:
    flake-parts.lib.mkFlake { inherit inputs; } {
      systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ];
      perSystem = { pkgs, ... }: {
        devShells.default = pkgs.mkShell {
          packages = [ pkgs.hello ];
        };
      };
    };
}
```

This even has the benefit that it will work on all the systems specified.

<Draft>

# Evaluated Flake top level

You can inspect your flake like this:

```shell
nix repl .
> :load-flake . # shorthand ':lf .'
```

```nix
{
  _type = "flake";
  inputs = { ... };
  outputs = { ... };
  sourceInfo = { ... };
}
```

</Draft>

<References>
<ReferenceLink href="https://www.youtube.com/watch?v=JCeYq72Sko0">vimjoyer flakes guide</ReferenceLink>
</References>

An entry point for Nix stuff. # Writing your first flake Your flake is the entry point to your application built and run with nix. Your flake is a function from `inputs` (which consist of urls for getting package sets) to `outputs`, with outputs being one big _attribute set_. This `outputs` _attribute set_ looks something like this (very much like a JSON object): ```nix # outputs { apps = { ... }; checks = { ... }; devShells = { ... }; formatter = { ... }; legacyPackages = { ... }; nixosConfigurations = { ... }; nixosModules = { ... }; overlays = { ... }; packages = { ... }; } ``` Every key in here is a special, magic key that Nix interprets in a certain way. For the minimal (non-functional) flake: ```nix { inputs = {}; outputs = {...}: {}; } ``` all of them are empty. Through populating them, we give our Nix Flake additional capabilities. The first capability to give our Nix Flakes is normally a _development shell_. In Nix Language, we create this like this: ```nix title="flake.nix" { inputs = { nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; }; outputs = { nixpkgs, ... }: { devShells.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.mkShell {}; }; } ``` And run it via `nix develop` (getting it? The `devShells` output gets run by `nix develop`): ```shell > nix develop (nix-shell-env)> ``` But that doesn't do anything yet. For it to do something we need to add something into the devshell: ```nix title="flake.nix" del={7} ins={8-10} { inputs = { nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; }; outputs = { nixpkgs, ... }: { devShells.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.mkShell {}; devShells.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.mkShell { packages = [ nixpkgs.legacyPackages.x86_64-linux.hello ]; }; }; } ``` (Run `nix develop`, and then we have the `hello` executable available.): ```shell > nix develop (nix-shell-env)> hello Hello, world! ``` Let's clean this up a bit, and then we're done for now. There's lots of repetition in the current file, and the solution to this is not as easy as inserting a `let` block. The accepted and easily extensible solution for this is flake-parts, but their homepage is pretty hard to understand at first. We want to translate our example to this: ```nix title="flake.nix" { inputs = { nixpkgs.url = "https://github.com/nixos/nixpkgs?ref=nixos-unstable"; }; outputs = inputs@{ flake-parts, ... }: flake-parts.lib.mkFlake { inherit inputs; } { systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ]; perSystem = { pkgs, ... }: { devShells.default = pkgs.mkShell { packages = [ pkgs.hello ]; }; }; }; } ``` This even has the benefit that it will work on all the systems specified. <Draft> # Evaluated Flake top level You can inspect your flake like this: ```shell nix repl . > :load-flake . # shorthand ':lf .' ``` ```nix { _type = "flake"; inputs = { ... }; outputs = { ... }; sourceInfo = { ... }; } ``` </Draft> <References> <ReferenceLink href="https://www.youtube.com/watch?v=JCeYq72Sko0">vimjoyer flakes guide</ReferenceLink> </References>

Especially this here for Nix Flakes :)

#Nix #Simplicity #FunctionalProgramming

20.02.2026 16:38 πŸ‘ 0 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0

Originally posted at https://beathagenlocher.com/nix

20.02.2026 16:36 πŸ‘ 0 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0
# Install Nix

The installer on the website sucks (doesn't work with SELinux, for example), but there is a community-maintained one:

```shell
curl -fsSL https://artifacts.nixos.org/nix-installer | sh -s -- install
```

With that completed, go write your first Nix Flake

# Install Nix The installer on the website sucks (doesn't work with SELinux, for example), but there is a community-maintained one: ```shell curl -fsSL https://artifacts.nixos.org/nix-installer | sh -s -- install ``` With that completed, go write your first Nix Flake

I've added a bit more barebones Nix stuff that can be confusing at first :)

#Nix #OS #ProgrammingLanguage #nixpkgs #NixOS #FunctionalProgramming #DeclarativeProgramming

20.02.2026 16:36 πŸ‘ 0 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0

Well, this is how it starts.. ^^

20.02.2026 13:30 πŸ‘ 3 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0

Never heard of webrings before. Thanks for writing this up!

19.02.2026 17:06 πŸ‘ 2 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0

Sure! :)

16.02.2026 22:19 πŸ‘ 0 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0

Specifically constructions like

"Not x. Not y. Just z"

"That's not x. It's y"

and similar :)

16.02.2026 22:10 πŸ‘ 1 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0

Just leaving that here for feedback (with which you can do what you want :)) :
Your posts sound pretty AI-sloppy to me.

(Though I can't say I'm getting more engagement with mine 😬)

Like the idea of posting every day very much, though :)

16.02.2026 21:35 πŸ‘ 1 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0

Originally posted at https://beathagenlocher.com/stream#00123

16.02.2026 09:58 πŸ‘ 0 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0
I did not know it's a thing that people are building desktop environments in the browser for fun:

- https://webos.js.org/
- https://dustinbrett.com/

Wild.

I did not know it's a thing that people are building desktop environments in the browser for fun: - https://webos.js.org/ - https://dustinbrett.com/ Wild.

Apparently this is a thing: Desktop Environments in the browser

#OS #TheWeb

16.02.2026 09:58 πŸ‘ 0 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0

Originally posted at https://beathagenlocher.com/stream#00122

15.02.2026 17:10 πŸ‘ 0 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0