<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tömő Viktor</title>
    <description>The latest articles on DEV Community by Tömő Viktor (@tomoviktor).</description>
    <link>https://dev.to/tomoviktor</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1080642%2F87561358-26bd-4214-9ea9-c1b9d22fb017.png</url>
      <title>DEV Community: Tömő Viktor</title>
      <link>https://dev.to/tomoviktor</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tomoviktor"/>
    <language>en</language>
    <item>
      <title>Best Way to Open URLs in Your Terminal via Tmux</title>
      <dc:creator>Tömő Viktor</dc:creator>
      <pubDate>Sun, 07 Jul 2024 19:18:56 +0000</pubDate>
      <link>https://dev.to/tomoviktor/best-way-to-open-urls-in-your-terminal-via-tmux-595b</link>
      <guid>https://dev.to/tomoviktor/best-way-to-open-urls-in-your-terminal-via-tmux-595b</guid>
      <description>&lt;p&gt;Efficently open URLs by only using the keyboard. Let me show you the script I made.&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;I have been using &lt;a href="https://github.com/tmux/tmux/wiki" rel="noopener noreferrer"&gt;tmux&lt;/a&gt; for a while now. It was really worth it, especially after I started using &lt;a href="https://neovim.io/" rel="noopener noreferrer"&gt;neovim&lt;/a&gt;. One thing was really missing though compared to my previous setup and that was opening URLs. I always used my mouse to do that, but now I couldn't even do that because I am using the good old &lt;a href="https://invisible-island.net/xterm/" rel="noopener noreferrer"&gt;xterm&lt;/a&gt; as a terminal which doesn't have a built in feature like that.&lt;/p&gt;

&lt;p&gt;I looked at some xterm solutions for this problem, but I knew I had to do something in tmux because it has lots of commands and such. Then I found &lt;a href="https://github.com/tmux-plugins/tmux-urlview" rel="noopener noreferrer"&gt;&lt;code&gt;tmux-urlview&lt;/code&gt;&lt;/a&gt;, so I was sure I can make something cool with tmux.&lt;/p&gt;

&lt;h2&gt;
  
  
  The script
&lt;/h2&gt;

&lt;p&gt;The full script can be found &lt;a href="https://github.com/11Firefox11/.dotfiles/blob/84f80130b9f5c5b21ebfceb4cc9b3b7b232b6e51/bin/.local/scripts/tmux-select-url" rel="noopener noreferrer"&gt;on my GitHub in my &lt;code&gt;.dotfiles&lt;/code&gt; repository's &lt;code&gt;scripts&lt;/code&gt; directory named &lt;code&gt;tmux-select-url&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First I had to understand the basics of the upper mentioned &lt;code&gt;tmux-urlview&lt;/code&gt;. It used a basic logic: save the current tmux pane's text content (buffer) and then parse out the URLs and show a choosing menu by which the URL will be opened. So I implemented the same logic.&lt;/p&gt;

&lt;p&gt;Starting with saving the current buffer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tmux capture-pane
tmux save-buffer &lt;span class="s2"&gt;"/tmp/tmuxsave"&lt;/span&gt;
tmux delete-buffer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now comes some basic Linux command knowledge: get the URLs out of the file (&lt;code&gt;/tmp/tmuxsave&lt;/code&gt;). That means we have to use some regex, but thankfully there are plenty out there on the internet, so we don't have to write our own. Just so duplicates are removed I also used &lt;code&gt;uniq&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;urls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-Eoi&lt;/span&gt; &lt;span class="s2"&gt;"(http|https)://[a-zA-Z0-9+./?=_%:-]+"&lt;/span&gt; /tmp/&lt;span class="nv"&gt;$buffer_file_name&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have the URLs in a variable. I had to figure out a cool way to select from them. It had to be quick and easy. My first thought was to use &lt;a href="https://github.com/junegunn/fzf" rel="noopener noreferrer"&gt;&lt;code&gt;fzf&lt;/code&gt;&lt;/a&gt; because I &lt;a href="https://tomoviktor.com/posts/tmux-useful-keybinds" rel="noopener noreferrer"&gt;already used it many times&lt;/a&gt; with tmux, but then I also stumbled up on tmux's &lt;a href="https://man7.org/linux/man-pages/man1/tmux.1.html#STATUS_LINE" rel="noopener noreferrer"&gt;&lt;code&gt;display-menu&lt;/code&gt;&lt;/a&gt; which you can see in this post's thumbnail. &lt;/p&gt;

&lt;p&gt;I decided to use both. The menu is good to display few links and it's really nice because it also provides a way to assign keybinds to items. In the menu I also made it so the &lt;code&gt;http&lt;/code&gt; or &lt;code&gt;https&lt;/code&gt; is cut off so it is better for my eyes. For more than 5 items I decided to use &lt;code&gt;fzf&lt;/code&gt; opened in a new pane.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;url_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$urls&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$url_count&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 5 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;tmux neww &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"url-select"&lt;/span&gt; &lt;span class="s2"&gt;"printf '%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;' '&lt;/span&gt;&lt;span class="nv"&gt;$urls&lt;/span&gt;&lt;span class="s2"&gt;' | fzf --exit-0 --print-query | xargs -I {} sh -c 'xdg-open {} &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;amp; tmux display-message &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;#[bold]&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;: Opened {}#[bold]&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;
&lt;span class="k"&gt;else
  &lt;/span&gt;&lt;span class="nv"&gt;tmux_menu&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"tmux display-menu -t 1 -T 'url-select'"&lt;/span&gt;
  &lt;span class="nv"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
  &lt;span class="k"&gt;for &lt;/span&gt;url &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;(f)urls&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nv"&gt;clean_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="p"&gt;#*//&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;tmux_menu&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$tmux_menu&lt;/span&gt;&lt;span class="s2"&gt; '&lt;/span&gt;&lt;span class="nv"&gt;$clean_url&lt;/span&gt;&lt;span class="s2"&gt;' &lt;/span&gt;&lt;span class="nv"&gt;$index&lt;/span&gt;&lt;span class="s2"&gt; 'run-shell &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;xdg-open &lt;/span&gt;&lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt; /dev/null 2&amp;gt;&amp;amp;1; tmux display-message '&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s2"&gt;'#[bold]&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;: Opened &lt;/span&gt;&lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="s2"&gt;#[bold]'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;

    &lt;span class="o"&gt;((&lt;/span&gt;index++&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$index&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 9 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nb"&gt;break
    &lt;/span&gt;&lt;span class="k"&gt;fi
  done

  &lt;/span&gt;&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="nv"&gt;$tmux_menu&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code upper isn't hard to understand, it's just dense. If you don't understand something I recommend you to read the manuals via &lt;a href="https://man7.org/linux/man-pages/man1/man.1.html" rel="noopener noreferrer"&gt;&lt;code&gt;man&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I added some useful things to the script too. For example if there are no URLs don't show anything or if only one is available just open it right away.&lt;/p&gt;

&lt;p&gt;By default &lt;code&gt;capture-buffer&lt;/code&gt; only captures the currently visible parts of the pane, but by setting &lt;code&gt;S&lt;/code&gt; to &lt;code&gt;-&lt;/code&gt; the whole history is saved. So in the final version of the script I added that if &lt;code&gt;"full"&lt;/code&gt; is passed as an argument then it will capture the full history. The full script can be found &lt;a href="https://github.com/11Firefox11/.dotfiles/blob/84f80130b9f5c5b21ebfceb4cc9b3b7b232b6e51/bin/.local/scripts/tmux-select-url" rel="noopener noreferrer"&gt;on my GitHub in my &lt;code&gt;.dotfiles&lt;/code&gt; repository's &lt;code&gt;scripts&lt;/code&gt; directory named &lt;code&gt;tmux-select-url&lt;/code&gt;&lt;/a&gt;. Feel free to use my script.&lt;/p&gt;

&lt;p&gt;To make it easily accessable I &lt;a href="https://github.com/11Firefox11/.dotfiles/blob/84f80130b9f5c5b21ebfceb4cc9b3b7b232b6e51/tmux/.tmux.conf#L5-L6" rel="noopener noreferrer"&gt;binded it to &lt;code&gt;o&lt;/code&gt; and &lt;code&gt;O&lt;/code&gt; in my &lt;code&gt;.tmux.conf&lt;/code&gt;&lt;/a&gt; (NOTE: I also added executable permission to the script and put it in my &lt;code&gt;$PATH&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bind-key o run-shell -b "tmux-select-url"
bind-key O run-shell -b "tmux-select-url full"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;I hope that this post encourages you to make your own tmux related scripts. &lt;/p&gt;

&lt;p&gt;If you are interested in more of this type of content then I suggest you to read posts from &lt;a href="https://tomoviktor.com/series/developer-productivity/page/1/" rel="noopener noreferrer"&gt;my series about improving my setup and developer productivity&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>tmux</category>
      <category>fzf</category>
      <category>linux</category>
    </item>
    <item>
      <title>Two Simple Tmux Keybinds that Help Me Everyday</title>
      <dc:creator>Tömő Viktor</dc:creator>
      <pubDate>Sun, 07 Jul 2024 19:12:53 +0000</pubDate>
      <link>https://dev.to/tomoviktor/two-simple-tmux-keybinds-that-help-me-everyday-1k3j</link>
      <guid>https://dev.to/tomoviktor/two-simple-tmux-keybinds-that-help-me-everyday-1k3j</guid>
      <description>&lt;p&gt;I have been using tmux for a while now, but I still need some help. Two keybinds help me day by day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Helpful keybinds
&lt;/h2&gt;

&lt;p&gt;They are literally helpful. When I started using &lt;a href="https://www.redhat.com/sysadmin/introduction-tmux-linux" rel="noopener noreferrer"&gt;tmux&lt;/a&gt; I of course didn't know any keybinds. There is a builtin help menu by using the keybind &lt;code&gt;?&lt;/code&gt;, but I wanted to level that up.&lt;/p&gt;

&lt;p&gt;All the keybinds are listed there, but I can't search them and manually looking through each one is frustrating. So I made one that is searchable via &lt;a href="https://junegunn.github.io/fzf/" rel="noopener noreferrer"&gt;&lt;code&gt;fzf&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;unbind-key ?
bind-key ? run-shell &lt;span class="s1"&gt;'tmux neww -n "help"  "tmux list-keys -N | fzf"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The binding opens a new tmux window with the name help and it lists all the available keybinds and pipes it into fzf. With this I can quickly search for something like "split vertically".&lt;/p&gt;

&lt;p&gt;There is more! I use &lt;a href="https://zimfw.sh/" rel="noopener noreferrer"&gt;&lt;code&gt;zimfw&lt;/code&gt;&lt;/a&gt; which is like &lt;a href="https://ohmyz.sh/" rel="noopener noreferrer"&gt;&lt;code&gt;oh-my-zsh&lt;/code&gt;&lt;/a&gt; but faster. I discovered that it comes with default &lt;a href="https://www.man7.org/linux/man-pages/man1/alias.1p.html" rel="noopener noreferrer"&gt;aliases&lt;/a&gt;. I want to learn those too because &lt;code&gt;Gcm&lt;/code&gt; is faster than &lt;code&gt;git commit --message&lt;/code&gt;. So I made a quick fzf bind for that too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bind-key a run-shell &lt;span class="s1"&gt;'tmux neww -n "aliases" "source ~/.zshrc &amp;amp;&amp;amp; alias | fzf"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here it's a good thing to know that by using &lt;code&gt;alias&lt;/code&gt; all the configured aliases are listed to stdout. Also, if you use &lt;a href="https://en.wikipedia.org/wiki/Bash_(Unix_shell)" rel="noopener noreferrer"&gt;bash&lt;/a&gt; instead of zsh then change &lt;code&gt;.zshrc&lt;/code&gt; to &lt;code&gt;.bashrc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Put the upper mentioned two keybinds into your &lt;code&gt;.tmux.conf&lt;/code&gt; and start learning the shortcuts which make your workflow faster!&lt;/p&gt;

&lt;p&gt;If you are interested in more of this type of content then I suggest you to read posts from &lt;a href="https://tomoviktor.com/series/developer-productivity/page/1/" rel="noopener noreferrer"&gt;my series about improving my setup and developer productivity&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>fzf</category>
      <category>tmux</category>
      <category>keybinds</category>
    </item>
    <item>
      <title>Simple Directory Watcher to Restart Dev Server</title>
      <dc:creator>Tömő Viktor</dc:creator>
      <pubDate>Thu, 27 Jun 2024 17:44:59 +0000</pubDate>
      <link>https://dev.to/tomoviktor/simple-directory-watcher-to-restart-dev-server-m4f</link>
      <guid>https://dev.to/tomoviktor/simple-directory-watcher-to-restart-dev-server-m4f</guid>
      <description>&lt;p&gt;Simple Linux script that restarts the dev server on file changes. Couldn't find it so I made it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;I have been learning &lt;a href="https://go.dev/" rel="noopener noreferrer"&gt;Go&lt;/a&gt; and I came across a pretty basic problem. I was practicing making a REST API web server and I wanted to enable hot reloading so my changes would be visible while I am changing the code. This is a common workflow when using a development server. &lt;/p&gt;

&lt;p&gt;Ever since I have been making APIs there was always a simple way to enable hot reloading. It is easy in Go too, you just have to use &lt;a href="https://github.com/air-verse/air" rel="noopener noreferrer"&gt;air&lt;/a&gt;. It so simple, just write &lt;code&gt;air&lt;/code&gt; and you have hot reloading. Now you may ask the question that if it's so great what this post is for?&lt;/p&gt;

&lt;h2&gt;
  
  
  My script
&lt;/h2&gt;

&lt;p&gt;It all started when &lt;code&gt;air&lt;/code&gt; decided to freeze multiple times. I searched a few and found no quick solution so I started to think about a solution. By my understanding air basically just executes the command that runs the web server (in this case &lt;code&gt;go run .&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;Do I really need a whole Go library to do that? There must be a lighter or in other words Linux solution. I came across few solutions but not all had the capability to handle &lt;em&gt;all&lt;/em&gt; types of directory changes. I wanted to be able to watch for file: create, edit, move, delete. For example &lt;a href="https://github.com/eradman/entr" rel="noopener noreferrer"&gt;&lt;code&gt;entr&lt;/code&gt;&lt;/a&gt; doesn't rerun the command when a new file is added to the directory that is being watched.&lt;/p&gt;

&lt;p&gt;Then I discovered &lt;a href="https://github.com/inotify-tools/inotify-tools" rel="noopener noreferrer"&gt;&lt;code&gt;inotify-tools&lt;/code&gt;&lt;/a&gt; and inside it &lt;a href="https://linux.die.net/man/1/inotifywait" rel="noopener noreferrer"&gt;&lt;code&gt;inotifywait&lt;/code&gt;&lt;/a&gt;. This tool can do all kinds of file changes that I wanted. So now I only had to create a script which can run the specified command and also is able to kill the process and rerun the command. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The script takes whatever args are passed into it and runs that and restarts it whenever a file change happens in the current directory. I also made it so via &lt;code&gt;--we-exclude=[PATHS]&lt;/code&gt; you can pass inotify what to &lt;a href="https://linux.die.net/man/1/inotifywait" rel="noopener noreferrer"&gt;exlude from watching&lt;/a&gt;.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env zsh&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; inotifywait &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;print &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="s2"&gt;"%F{red}Can't start: inotifywait not found.%f"&lt;/span&gt;
  &lt;span class="nb"&gt;exit
&lt;/span&gt;&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nv"&gt;we_exclude_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nv"&gt;command_to_exec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;arg &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$arg&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"--we-exclude="&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nv"&gt;we_exclude_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;#*=&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;break
    &lt;/span&gt;&lt;span class="k"&gt;else
      &lt;/span&gt;command_to_exec+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$arg&lt;/span&gt;&lt;span class="s2"&gt; "&lt;/span&gt;
    &lt;span class="k"&gt;fi
done

&lt;/span&gt;&lt;span class="nv"&gt;command_to_exec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;command_to_exec&lt;/span&gt;&lt;span class="p"&gt;% &lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;print &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%T&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;: %F{green}Restarting...%f"&lt;/span&gt;
  &lt;span class="nv"&gt;$@&lt;/span&gt; &amp;amp;
  &lt;span class="nv"&gt;PID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$!&lt;/span&gt;
  inotifywait &lt;span class="nt"&gt;--event&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;modify,move,create,delete,delete_self &lt;span class="nt"&gt;--recursive&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--exclude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$we_exclude_value&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1
  pkill &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="nv"&gt;$PID&lt;/span&gt; 2&amp;gt;/dev/null
  &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nv"&gt;$PID&lt;/span&gt; 2&amp;gt;/dev/null
  &lt;span class="nb"&gt;wait&lt;/span&gt; &lt;span class="nv"&gt;$PID&lt;/span&gt; 2&amp;gt;/dev/null
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script is also &lt;a href="https://github.com/11Firefox11/.dotfiles/blob/main/bin/.local/scripts/watch-execute" rel="noopener noreferrer"&gt;available in my GitHub .dotfiles repository under the scripts directory&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you are interested in more of this type of content then I suggest you to read posts from &lt;a href="https://tomoviktor.com/series/developer-productivity/page/1/" rel="noopener noreferrer"&gt;my series about improving my setup and developer productivity&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>zsh</category>
      <category>linux</category>
      <category>development</category>
      <category>scripting</category>
    </item>
    <item>
      <title>Automatic Visual Feedback for System Volume Change in I3wm via Dunst</title>
      <dc:creator>Tömő Viktor</dc:creator>
      <pubDate>Thu, 27 Jun 2024 17:39:46 +0000</pubDate>
      <link>https://dev.to/tomoviktor/automatic-visual-feedback-for-system-volume-change-in-i3wm-via-dunst-4lo9</link>
      <guid>https://dev.to/tomoviktor/automatic-visual-feedback-for-system-volume-change-in-i3wm-via-dunst-4lo9</guid>
      <description>&lt;p&gt;Simple yet powerful all in one stytem volume watcher and changer script for linux. Let me show you my small script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I switched to the &lt;a href="https://i3wm.org/"&gt;i3 tiling based window manager&lt;/a&gt;. Because it's a whole different environment and thinking, it was very different from what I was used to. The volume buttons were working on my keyboard, but I didn't get any visual feedback. Furthermore, the volume percentage could go down below zero and increase up to more than hundread percent. There were times when I was confused why the keys stopped working, but the actual hidden reason was that the volume's value was &lt;em&gt;somehow -500 percent&lt;/em&gt;, so increasing it by 5 percent via my keys would have taken a little time.&lt;/p&gt;

&lt;p&gt;To solve all this, I decided to write my own &lt;a href="https://en.wikipedia.org/wiki/Z_shell"&gt;zsh&lt;/a&gt; script. If you are familiar with linux scripting you may ask: why didn't I use &lt;a href="https://en.wikipedia.org/wiki/Bash_(Unix_shell)"&gt;bash&lt;/a&gt;? It's simple, I did lots of bash scripting in school already so I decided to try out zsh (not like I discovered big differences).&lt;/p&gt;

&lt;p&gt;The script is available at &lt;a href="https://github.com/11Firefox11/.dotfiles/blob/e3ef385b969f1d5a0a9f81ffcf3ac0c057697eb1/bin/.local/scripts/change-volume"&gt;my GitHub .dotfiles repository named &lt;code&gt;change-volume&lt;/code&gt;&lt;/a&gt;. In this blog I will explain how to use it and how does the code work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the script
&lt;/h2&gt;

&lt;p&gt;There are two use cases for the script: watch, change the current volume. &lt;/p&gt;

&lt;p&gt;Watch listens to volume changes and automatically shows notifications. The &lt;em&gt;watcher actually watches&lt;/em&gt; meaning it also works if the volume isn't changed via this script.&lt;/p&gt;

&lt;p&gt;Chaning the volume is just a wrapper that takes care of minimizing and maximizing the values so they don't go under or over a certain limit. You must decrease or increase by percentage and you also have the option to mute too. When the source is muted and a value change is requested the script first unmutes the source and the next volume change will actually do something with the source's value.&lt;/p&gt;

&lt;p&gt;Starting a watch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;volume-changer &lt;span class="s2"&gt;"watch"&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Increase or decrease the volume or mute it fully (which automatically toggles):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;volume-changer &lt;span class="s2"&gt;"+5%"&lt;/span&gt;
volume-changer &lt;span class="s2"&gt;"+50%"&lt;/span&gt;
volume-changer &lt;span class="s2"&gt;"-5%"&lt;/span&gt;
volume-changer &lt;span class="s2"&gt;"-21%"&lt;/span&gt;

volume-changer &lt;span class="s2"&gt;"full"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My script also logs details via &lt;a href="https://man7.org/linux/man-pages/man1/logger.1.html"&gt;&lt;code&gt;logger&lt;/code&gt;&lt;/a&gt; so I can inspect it if it doesn't seem to work.&lt;/p&gt;

&lt;p&gt;If you think don't want to bother with the code, you can just &lt;a href="https://github.com/11Firefox11/.dotfiles/blob/e3ef385b969f1d5a0a9f81ffcf3ac0c057697eb1/bin/.local/scripts/change-volume"&gt;download it from GitHub&lt;/a&gt;. I made it so that in the &lt;a href="https://github.com/11Firefox11/.dotfiles/blob/e3ef385b969f1d5a0a9f81ffcf3ac0c057697eb1/bin/.local/scripts/change-volume#L2-L12"&gt;top few lines&lt;/a&gt; you can easily configure few basic things. Don't forget to download the &lt;a href="https://github.com/11Firefox11/.dotfiles/tree/e3ef385b969f1d5a0a9f81ffcf3ac0c057697eb1/assets/dotfile-assets"&gt;icons&lt;/a&gt; too if you need them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Sending notifications
&lt;/h3&gt;

&lt;p&gt;The base of all of this is notifications. Because my i3 came with &lt;a href="https://dunst-project.org/"&gt;dunst&lt;/a&gt; and I liked the simple look of it I decided to use it as the notification &lt;a href="https://en.wikipedia.org/wiki/Daemon_(computing)"&gt;daemon&lt;/a&gt;. I wanted to have 3 simple things: display current status of the volume via text, display an icon so it is somewhat prettier, display the volume level via a progress bar. Lucily all these are possible via dunst.&lt;/p&gt;

&lt;p&gt;How do you actually send notifications? Just use &lt;a href="https://linuxcommandlibrary.com/man/dunstify"&gt;dunstify&lt;/a&gt;. I also found a &lt;a href="https://web.archive.org/web/20240402025250/https://smarttech101.com/how-to-send-notifications-in-linux-using-dunstify-notify-send/"&gt;nice website that uses examples to show how it works&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Important note is that the progress bar feature is available since &lt;a href="https://github.com/dunst-project/dunst/releases/tag/v1.6.0"&gt;dunst version 1.6.0&lt;/a&gt;, so make sure you have a updated version. For me, the &lt;code&gt;apt install&lt;/code&gt; on my Ubuntu downloaded a very outdated version of dunst which din't supported progress bars, so I decided to &lt;a href="https://github.com/dunst-project/dunst/issues/1321"&gt;build it from source&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I created a perfect function for showing alerts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;lastalerttext&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
show_alert&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$lastalerttext&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nv"&gt;$3&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;lastalerttext&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    dunstify &lt;span class="nt"&gt;--replace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1111 &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1500 &lt;span class="nt"&gt;--icon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--hints&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;int:value:&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt; &lt;span class="s2"&gt;"change-volume"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# usage: show_alert [iconpath] [progressbar percentage] [text]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Chaning the volume
&lt;/h3&gt;

&lt;p&gt;I wanted to make it so you must provide two type of values for changing the percentage: &lt;code&gt;+[NUMBER]%&lt;/code&gt; or &lt;code&gt;-[NUMBER]%&lt;/code&gt;. For these format validations I made three functions (&lt;code&gt;starts_with_pm&lt;/code&gt;, &lt;code&gt;ends_with_percent_and_numeric&lt;/code&gt;, &lt;code&gt;extract_number&lt;/code&gt;) which I won't describe here, but you can &lt;a href="https://github.com/11Firefox11/.dotfiles/blob/e3ef385b969f1d5a0a9f81ffcf3ac0c057697eb1/bin/.local/scripts/change-volume#L15-L42"&gt;view that on my GitHub&lt;/a&gt; (I also used them via if statements).&lt;/p&gt;

&lt;p&gt;To actually change the values I used &lt;a href="https://linux.die.net/man/1/pactl"&gt;&lt;code&gt;pactl&lt;/code&gt;&lt;/a&gt;. It is very simple to use. You can even use &lt;code&gt;@DEFAULT_SINK@&lt;/code&gt; to not bother with getting the current source (sink) that you are making the changes to. Set volume with &lt;code&gt;set-sink-volume&lt;/code&gt; and mute with &lt;code&gt;set-sink-mute&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;One last thing: just as I mentioned upper if the volume is muted then first I unmute.&lt;/p&gt;

&lt;p&gt;I also crafted this into a function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;set_volume&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"full"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;pactl set-sink-mute @DEFAULT_SINK@ toggle
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nv"&gt;muted&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;pactl get-sink-mute @DEFAULT_SINK@ | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$muted&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"yes"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;set_volume &lt;span class="s2"&gt;"full"&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;pactl set-sink-volume @DEFAULT_SINK@ &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt;
    &lt;span class="k"&gt;fi
  fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# usage: set_volume ["full" or number]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make this work via command line and to also minimize and maximize the values I used few if statements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;minvolume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="nv"&gt;maxvolume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;150
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="nv"&gt;volume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$volume&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"full"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;set_volume &lt;span class="nv"&gt;$volume&lt;/span&gt;
  &lt;span class="nb"&gt;exit
&lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="nv"&gt;changeval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;extract_number &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$volume&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;currvolume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;extract_number &lt;span class="si"&gt;$(&lt;/span&gt;pactl get-sink-volume @DEFAULT_SINK@ | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $5}'&lt;/span&gt;&lt;span class="si"&gt;))&lt;/span&gt;
&lt;span class="nv"&gt;finalvolume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="nv"&gt;$currvolume&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;$changeval&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="nv"&gt;$finalvolume&lt;/span&gt; &amp;lt; &lt;span class="nv"&gt;$minvolume&lt;/span&gt; &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="c"&gt;# if goes under min then use the min value&lt;/span&gt;
  &lt;span class="nv"&gt;finalvolume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$minvolume&lt;/span&gt;
&lt;span class="k"&gt;fi
if&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="nv"&gt;$finalvolume&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$maxvolume&lt;/span&gt; &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="c"&gt;# if goes over max then use the max value&lt;/span&gt;
  &lt;span class="nv"&gt;finalvolume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$maxvolume&lt;/span&gt;
&lt;span class="k"&gt;fi
&lt;/span&gt;set_volume &lt;span class="nv"&gt;$finalvolume&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Watching for change
&lt;/h2&gt;

&lt;p&gt;Now comes the final part. I start by listening to events via &lt;code&gt;pactl subscribe&lt;/code&gt; and via a while loop I display notifications based on if it's muted or what's the current volume percentage.&lt;/p&gt;

&lt;p&gt;Because now I will need icons I &lt;a href="https://github.com/11Firefox11/.dotfiles/tree/e3ef385b969f1d5a0a9f81ffcf3ac0c057697eb1/assets/dotfile-assets"&gt;downloaded 4 types of them&lt;/a&gt;: low, mid, high, muted. I also added variables to my script which decides the border limits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;highafter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;75
&lt;span class="nv"&gt;midafter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;35

&lt;span class="nv"&gt;muteimg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/dotfile-assets/volume-mute.svg"&lt;/span&gt;
&lt;span class="nv"&gt;highimg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/dotfile-assets/volume-high.svg"&lt;/span&gt;
&lt;span class="nv"&gt;lowimg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/dotfile-assets/volume-low.svg"&lt;/span&gt;
&lt;span class="nv"&gt;midimg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/dotfile-assets/volume-mid.svg"&lt;/span&gt;

get_icon_from_value&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &amp;lt; &lt;span class="nv"&gt;$midafter&lt;/span&gt; &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$lowimg&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &amp;lt; &lt;span class="nv"&gt;$highafter&lt;/span&gt; &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$midimg&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$highimg&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I could easily display alerts. The while looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ...&lt;/span&gt;
pactl subscribe | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;--line-buffered&lt;/span&gt; &lt;span class="s2"&gt;"sink"&lt;/span&gt; |
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;muted&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;pactl get-sink-mute @DEFAULT_SINK@ | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$muted&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"yes"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;show_alert &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$muteimg&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"0"&lt;/span&gt; &lt;span class="s2"&gt;"Volume: Muted"&lt;/span&gt;
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nv"&gt;currvolume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;get_curr_volume&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;icontoshow&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;get_icon_from_value &lt;span class="nv"&gt;$currvolume&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    show_alert &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$icontoshow&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$currvolume&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"Volume: &lt;/span&gt;&lt;span class="nv"&gt;$currvolume&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt;
  &lt;span class="k"&gt;fi
done
&lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Making it work
&lt;/h2&gt;

&lt;p&gt;The script is ready. All that is left is &lt;a href="https://i3wm.org/docs/userguide.html#_automatically_starting_applications_on_i3_startup"&gt;making i3 start the watch on startup&lt;/a&gt; and &lt;a href="https://i3wm.org/docs/userguide.html#keybindings"&gt;adding keybinds that use the script&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;I won't detail them here because i3 has great documentation about it. If you are curious you can view my i3 config to see &lt;a href="https://github.com/11Firefox11/.dotfiles/blob/e3ef385b969f1d5a0a9f81ffcf3ac0c057697eb1/i3/.config/i3/config#L25-L29"&gt;where I created the keybindings&lt;/a&gt; and where &lt;a href="https://github.com/11Firefox11/.dotfiles/blob/e3ef385b969f1d5a0a9f81ffcf3ac0c057697eb1/i3/.config/i3/config#L213"&gt;I added the autostart&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you are interested more of this content then I suggest you to read posts from &lt;a href="https://tomoviktor.com/series/developer-productivity/page/1/"&gt;my series about improving my setup and developer productivity&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>i3</category>
      <category>dunst</category>
      <category>dunstify</category>
    </item>
    <item>
      <title>Simple markdown plugin to open external links in a new tab</title>
      <dc:creator>Tömő Viktor</dc:creator>
      <pubDate>Sun, 04 Jun 2023 18:35:32 +0000</pubDate>
      <link>https://dev.to/tomoviktor/simple-markdown-plugin-to-open-external-links-in-a-new-tab-2583</link>
      <guid>https://dev.to/tomoviktor/simple-markdown-plugin-to-open-external-links-in-a-new-tab-2583</guid>
      <description>&lt;p&gt;I made all the external links to be &lt;code&gt;_blank&lt;/code&gt;. I needed this because I write every post of mine in Markdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;On my &lt;a href="https://tomoviktor.com"&gt;personal blog&lt;/a&gt; I have few external links in my posts. I wanted to keep people on my website by applying &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#target"&gt;&lt;code&gt;target="_blank"&lt;/code&gt;&lt;/a&gt; on external (those what don't reference to my site) links. This is a common and good practice too. I write my content in Markdown, so I decided to write a &lt;a href="https://remark.js.org/"&gt;&lt;code&gt;remark&lt;/code&gt;&lt;/a&gt; plugin. It is simple to implement, just few lines of code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The plugin
&lt;/h2&gt;

&lt;p&gt;It takes in the &lt;code&gt;tree&lt;/code&gt; of the markdown file and looks through the links. &lt;/p&gt;

&lt;p&gt;To check if it is an external site:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Checks with a regex if link is a full url (for example: &lt;code&gt;https://example.com/&lt;/code&gt; will be full but &lt;code&gt;/example&lt;/code&gt; won't be)&lt;/li&gt;
&lt;li&gt;Checks if it doesn't contain my site's starting url (edit this if you use the plugin)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If an external link is found, it sets the &lt;code&gt;target&lt;/code&gt; to &lt;code&gt;_blank&lt;/code&gt;. Because the element might not have any special property, first it ensures that it has &lt;code&gt;data&lt;/code&gt; and &lt;code&gt;data.hProperties&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;visit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unist-util-visit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;site&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://tomoviktor.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;externalAnchorPlugin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;https&lt;/span&gt;&lt;span class="se"&gt;?)&lt;/span&gt;&lt;span class="sr"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\/\/[^\s/&lt;/span&gt;&lt;span class="sr"&gt;$.?#&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;.&lt;/span&gt;&lt;span class="se"&gt;[^\s]&lt;/span&gt;&lt;span class="sr"&gt;*$/i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;??=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
        &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hProperties&lt;/span&gt; &lt;span class="o"&gt;??=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
        &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_blank&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use it with Astro
&lt;/h2&gt;

&lt;p&gt;As I mentioned in one of my &lt;a href="https://tomoviktor.com/posts/image-performance-beginning#note"&gt;other post&lt;/a&gt; I use the &lt;a href="https://astro.build/"&gt;Astro web framework&lt;/a&gt; for my blog. Astro makes it effortless to import a Markdown plugin. All I had to do is add the function to &lt;code&gt;astro.config.mjs&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;externalAnchorPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./remarkplugins/external-anchor-plugin.mjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;remarkPlugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;externalAnchorPlugin&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>astro</category>
      <category>markdown</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>CleverImage Astro Component for My Responsive Images</title>
      <dc:creator>Tömő Viktor</dc:creator>
      <pubDate>Fri, 12 May 2023 16:02:14 +0000</pubDate>
      <link>https://dev.to/tomoviktor/cleverimage-astro-component-for-my-responsive-images-3ml1</link>
      <guid>https://dev.to/tomoviktor/cleverimage-astro-component-for-my-responsive-images-3ml1</guid>
      <description>&lt;p&gt;Create responsive images in pure HTML with the CleverImage component. Let me show you how easy it is. The full code written in this post can be found at &lt;a href="https://gist.github.com/11Firefox11/817beec030bc911bad8c39816cde6d1e"&gt;GitHub Gist: CleverImg.astro&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a responsive image?
&lt;/h2&gt;

&lt;p&gt;Responsive images work well on all kinds of screen sizes and resolutions. It can help in optimizing page performance. For example, you don't need a 4k image on a mobile screen because it is just a waste.&lt;/p&gt;

&lt;p&gt;It can be done in pure HTML. To create one, you have to modify an &lt;code&gt;img&lt;/code&gt; element's attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#srcset"&gt;&lt;code&gt;srcset&lt;/code&gt;&lt;/a&gt;: specify multiple image sources and their scale factors (note that if &lt;code&gt;sizes&lt;/code&gt; isn't defined then the browser will decide which to display, that is why order matters here, the browser will use the first usable image it finds)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#sizes"&gt;&lt;code&gt;sizes&lt;/code&gt;&lt;/a&gt;: specify which image source to use from the &lt;code&gt;srcset&lt;/code&gt; on which screen sizes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These two attributes value generation will be implemented.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;In the &lt;a href="https://tomoviktor.com/posts/thumbnail-generation"&gt;previous part&lt;/a&gt; we created an automatic image generator, look at that for generating images, I am going to use an example related to that.&lt;/p&gt;

&lt;p&gt;Let's say our base image is &lt;code&gt;cat.png&lt;/code&gt;, it has different sizes and formats (file names scheme: &lt;code&gt;[NAME]-[WIDTH]-[HEIGHT].[EXTENSION]&lt;/code&gt;): &lt;code&gt;cat-100-500.png&lt;/code&gt;, &lt;code&gt;cat-100-500.webp&lt;/code&gt;, &lt;code&gt;cat-200-1000.png&lt;/code&gt;, &lt;code&gt;cat-200-1000.webp&lt;/code&gt;. The smaller image should be loaded if the user's screen is smaller than 768px. WebP is preferred.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; 
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"cat-200-1000.png"&lt;/span&gt;
  &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"a black cat with big green eyes"&lt;/span&gt;
  &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"cat-100-500.webp 100w,
          cat-100-500.png 100w,
          cat-200-1000.webp 200w,
          cat-200-1000.png 200w"&lt;/span&gt;
  &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 768px) 100px, 200px"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more explanation read &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images"&gt;MDN - Responsive images&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;Create a new &lt;a href="https://docs.astro.build/en/core-concepts/astro-components/"&gt;Astro component&lt;/a&gt;, let's name it &lt;code&gt;CleverImg.astro&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What &lt;a href="(https://docs.astro.build/en/core-concepts/astro-components/#component-props)"&gt;props&lt;/a&gt; are needed?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic &lt;code&gt;img&lt;/code&gt; tag arguments: &lt;code&gt;imgPath&lt;/code&gt; (&lt;code&gt;src&lt;/code&gt;), &lt;code&gt;alt&lt;/code&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#loading"&gt;&lt;code&gt;loading&lt;/code&gt;&lt;/a&gt; (if later want to add lazy load)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sizes&lt;/code&gt; what tells the sizes to generate for &lt;code&gt;srcset&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;breakpoints&lt;/code&gt; that declares at what &lt;code&gt;maxWidth&lt;/code&gt; which size source should be loaded, here &lt;code&gt;0&lt;/code&gt; &lt;code&gt;maxWidth&lt;/code&gt; will be the default non media query related size&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;withWebp&lt;/code&gt; if the image(s) exist in &lt;code&gt;.webp&lt;/code&gt; format too
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;imgPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eager&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lazy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}[],&lt;/span&gt;
  &lt;span class="nx"&gt;breakpoints&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;maxWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;imgWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;}[]&lt;/span&gt;
  &lt;span class="nx"&gt;withWebp&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;imgPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;withWebp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;breakpoints&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Astro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// init each prop as a const variable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both &lt;code&gt;srcset&lt;/code&gt; and &lt;code&gt;sizes&lt;/code&gt; require separating strings with a comma. That is why both will be handled as an array and will be &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join"&gt;joined&lt;/a&gt; after.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;generatedSrcset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;generatedSizes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you followed the &lt;a href="https://tomoviktor.com/posts/thumbnail-generation"&gt;previous part&lt;/a&gt;, and images are only generated on build, then put the following code into an if, so it only runs on build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PROD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Generate srcset
&lt;/h3&gt;

&lt;p&gt;First, sort the &lt;code&gt;sizes&lt;/code&gt;. Because order matters, order them ascending.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we have to append strings to &lt;code&gt;generatedSrcset&lt;/code&gt; in format &lt;code&gt;[IMGPATH] [IMGWIDTH]w&lt;/code&gt;. Path will be generated with function that was written in &lt;a href="(https://tomoviktor.com/posts/thumbnail-generation)"&gt;previous part of this series&lt;/a&gt;. This must be done for WebP format too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;withWebp&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;isWebp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// to do it for webp too&lt;/span&gt;
    &lt;span class="nx"&gt;generatedSrcset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ImageGenerator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imgPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isWebp&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;w`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is a small modification which should be made. If there is a &lt;code&gt;breakpoint&lt;/code&gt;, only include images which are in that. Because &lt;code&gt;breakpoint&lt;/code&gt; will tell the browser to display which image and when, just those images are required. For this, let's filter the sizes and remove ones that aren't required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;allBreakpointSizes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;breakpoints&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;allBreakpointSizes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;breakpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bp&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;bp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;imgWidth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allBreakpointSizes&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;allBreakpointSizes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt; &lt;span class="c1"&gt;// skip if the curren width is not found in breakpoints&lt;/span&gt;
  &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;withWebp&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;isWebp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;generatedSrcset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ImageGenerator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imgPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isWebp&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;w`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Generate sizes
&lt;/h3&gt;

&lt;p&gt;Add strings to &lt;code&gt;generatedSizes&lt;/code&gt; in &lt;code&gt;(max-width: [MAXWIDTH]px) {IMGWIDTH}px&lt;/code&gt; format. There is one exception, if the &lt;code&gt;maxWidth&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt; then it means that this is the default value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;breakpoints&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;breakpoint&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;breakpoints&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;breakpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxWidth&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;generatedSizes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;breakpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;imgWidth&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// handle default&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;generatedSizes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`(max-width: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;breakpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxWidth&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px) &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;breakpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;imgWidth&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use it in HTML
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;src&lt;/code&gt; should be the biggest sized image. If someone's browser doesn't support &lt;code&gt;srcset&lt;/code&gt; then this will be used. The other attributes are nothing special, the generated arrays must be joined with comma.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
// ...
const biggestSize = sizes.reduce((prev, current) =&amp;gt; {
  return (prev.width &amp;gt; current.width) ? prev : current;
});
---
&amp;lt;img
  src={ImageCompressorIntegration.generateName(imgPath, biggestSize)}
  alt={alt}
  srcset={generatedSrcset.join(", ")}
  sizes={generatedSizes.join(", ")}
  loading={loading}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you followed the &lt;a href="https://tomoviktor.com/posts/thumbnail-generation"&gt;previous part&lt;/a&gt;, and images are only generated on build, then put the fully generated &lt;code&gt;img&lt;/code&gt; in a conditional, and put a simple &lt;code&gt;img&lt;/code&gt; in with &lt;code&gt;import.meta.env.DEV&lt;/code&gt; condition. Look at the implementation at &lt;a href="https://gist.github.com/11Firefox11/817beec030bc911bad8c39816cde6d1e#file-cleverimg-astro-L39-L49"&gt;GitHub Gist: CleverImg.astro&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>astro</category>
      <category>performance</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Automatic Responsive Image Generation with an Astro Hook</title>
      <dc:creator>Tömő Viktor</dc:creator>
      <pubDate>Fri, 12 May 2023 16:01:49 +0000</pubDate>
      <link>https://dev.to/tomoviktor/automatic-responsive-image-generation-with-an-astro-hook-5b3o</link>
      <guid>https://dev.to/tomoviktor/automatic-responsive-image-generation-with-an-astro-hook-5b3o</guid>
      <description>&lt;p&gt;Resized, compressed, formatted pictures. Use an &lt;a href="https://astro.build/"&gt;Astro&lt;/a&gt; hook to automatically format images. I use typescript in this post. The full code written in this post can be found at &lt;a href="https://gist.github.com/11Firefox11/b371c3f6ad3acc8213772260fd8973e9"&gt;GitHub Gist: ImageCompressorIntegration.ts&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Get the most out of the images on your site. I &lt;a href="https://tomoviktor.com//posts/image-performance-beginning"&gt;optimized the thumbnails on my personal blog&lt;/a&gt;, this is the image generation part of it.&lt;/p&gt;

&lt;p&gt;The different methods I used to format the images:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Resize to different sizes&lt;/li&gt;
&lt;li&gt;Convert to &lt;a href="https://en.wikipedia.org/wiki/WebP"&gt;WebP&lt;/a&gt; format&lt;/li&gt;
&lt;li&gt;Compress&lt;/li&gt;
&lt;li&gt;Remove all metadata&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To actually generate the methods, I made an Astro hook that runs at build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing the methods
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;Let's use a class for this. Name it however you want, I used &lt;code&gt;ImageGenerator&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ImageGenerator&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One special custom type is required. Image related sizes will be used frequently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;imageSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To resize the images a naming convention must be created, because multiple images will be generated. The typical that is used is: &lt;code&gt;[NAME]-[WIDTH]-[HEIGHT].[EXTENSION]&lt;/code&gt;. For example if it resizes &lt;code&gt;image.png&lt;/code&gt; to 600 width and 315 height and changes its format to WebP the name of the image will be &lt;code&gt;image-600-315.webp&lt;/code&gt;. This name generation will be used frequently, so let's create a method for that. The &lt;code&gt;NAME&lt;/code&gt;, &lt;code&gt;WIDTH&lt;/code&gt; and &lt;code&gt;HEIGHT&lt;/code&gt;, &lt;code&gt;EXTENSION&lt;/code&gt; are parameters. Because at the end it will only use &lt;code&gt;wepb&lt;/code&gt;, instead of &lt;code&gt;EXTENSION&lt;/code&gt; I will use a boolean that indicates if the result file extension is &lt;code&gt;webp&lt;/code&gt; or not.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ImageGenerator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;generateName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;imageSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isWebp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;extension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isWebp&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.webp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;justName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt; &lt;span class="c1"&gt;// /directory/file.txt -&amp;gt; /directory/file&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;justName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// join the 3 parts and add extension&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am going to predefine the sizes of my thumbnails.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ImageGenerator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;thumbnailMetaSizes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;630&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;315&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;189&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;736&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;414&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To actually edit the images, we are going to use &lt;a href="https://sharp.pixelplumbing.com/"&gt;&lt;code&gt;sharp&lt;/code&gt;&lt;/a&gt;. Sharp is a high-performance image processing library. You can do all the cool things we want to do with it. &lt;a href="https://sharp.pixelplumbing.com/install"&gt;Install the library&lt;/a&gt; then import it with &lt;code&gt;import sharp from 'sharp'&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Process
&lt;/h3&gt;

&lt;p&gt;Let's edit the images.&lt;/p&gt;

&lt;p&gt;First, load in the image with sharp.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ImageGenerator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;processImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// without a filepath we can't open it :D&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to resize the image to multiple sizes and edit each of them. That is why a &lt;code&gt;imageSize[]&lt;/code&gt; parameter is needed. It is very easy to &lt;a href="https://sharp.pixelplumbing.com/api-resize"&gt;resize images with sharp&lt;/a&gt;, it also allows to select the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit"&gt;object-fit&lt;/a&gt; of the image. Object fit tells how the image should look like if the resized ratio is not the original one. With object fit, I can define that it will always show the center part of the image. We have to make sure to clone the image before doing anything with it, so the resize will always happen with the original one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ImageGenerator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;processImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;imageSize&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resizedImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cover&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;withoutEnlargement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="c1"&gt;// compress, remove metadata, save to png and webp&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, format the file to WebP. I want to leave this optional, so &lt;code&gt;needWebp&lt;/code&gt; argument will be added to our method definition. &lt;a href="https://sharp.pixelplumbing.com/api-output#toformat"&gt;&lt;code&gt;toFormat&lt;/code&gt; method&lt;/a&gt; is used for converting between formats.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ImageGenerator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;processImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;imageSize&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;needWebp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="c1"&gt;//...&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resizedImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="c1"&gt;//...&lt;/span&gt;
      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isWebp&lt;/span&gt; &lt;span class="k"&gt;of &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;needWebp&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// we are going to run compress and metadata modification for each format&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;resizedImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFormat&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;isWebp&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;webp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To compress, add &lt;code&gt;{quality: 100}&lt;/code&gt; as a parameter to &lt;code&gt;toFormat&lt;/code&gt; method. To remove metadata call &lt;code&gt;withMetadata&lt;/code&gt; after the formatting is done.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;resizedImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFormat&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;isWebp&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;webp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;withMetadata&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last thing is to save the new images. We already implemented &lt;code&gt;generateName&lt;/code&gt;, we just have to use it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ImageGenerator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;processImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;imageSize&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;needWebp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="c1"&gt;//...&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;basePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sep&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//...&lt;/span&gt;
      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isWebp&lt;/span&gt; &lt;span class="k"&gt;of &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;needWebp&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ImageCompressorIntegration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isWebp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;resizedImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFormat&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;isWebp&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;webp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;withMetadata&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I don't need the original image, so I added an option to delete it automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ImageGenerator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;processImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;imageSize&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;needWebp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;removeOriginal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;removeOriginal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// rm to remove&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are done with our &lt;code&gt;ImageGenerator&lt;/code&gt; class.&lt;/p&gt;

&lt;h3&gt;
  
  
  Astro hook
&lt;/h3&gt;

&lt;p&gt;To automatically generate the images a &lt;a href="https://docs.astro.build/en/reference/integrations-reference/#hooks"&gt;custom&lt;/a&gt; &lt;a href="https://docs.astro.build/en/guides/integrations-guide/#using-integrations"&gt;Astro integration&lt;/a&gt; is required.&lt;/p&gt;

&lt;p&gt;There are 3 build related hooks: &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;setup&lt;/code&gt;, &lt;code&gt;generated&lt;/code&gt;. We are going to use &lt;a href="https://docs.astro.build/en/reference/integrations-reference/#astrobuildgenerated"&gt;&lt;code&gt;astro:build:generated&lt;/code&gt;&lt;/a&gt;, so it will run after Astro is fully done with the build. &lt;/p&gt;

&lt;p&gt;Inside the same TypeScript file as our &lt;code&gt;ImageGenerator&lt;/code&gt; class export a function that will return the integration's settings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AstroIntegration&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;astro&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createPlugin&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;AstroIntegration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ImageCompressorIntegration&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;astro:build:generated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="c1"&gt;// make it async because our processImage is async too&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All we have to do is call &lt;code&gt;processImage&lt;/code&gt; for all images we want to process. I want to process all files in the &lt;code&gt;thumbnail&lt;/code&gt; directory. The built project's path can be accessed from the &lt;code&gt;options&lt;/code&gt; &lt;code&gt;dir&lt;/code&gt; argument.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createPlugin&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;AstroIntegration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ImageCompressorIntegration&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;astro:build:generated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;distPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allFiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readDirectoryRecursive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;distPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;thumbnail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;imgPath&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;allFiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ImageGenerator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;processImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imgPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ImageGenerator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;thumbnailMetaSizes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imgPath&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; image sizes generated (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;allFiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;readDirectoryRecursive&lt;/code&gt; function's code can be found at &lt;a href="https://gist.github.com/11Firefox11/b371c3f6ad3acc8213772260fd8973e9#file-imagecompressorintegration-ts-L24-L35"&gt;GitHub Gist: ImageCompressorIntegration.ts&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To finish it, &lt;a href="https://docs.astro.build/en/guides/integrations-guide/#using-integrations"&gt;use your integration in &lt;code&gt;astro.config.mjs&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;



&lt;p&gt;In the next part I will show how I use the images on the site.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>astro</category>
      <category>performance</category>
      <category>typescript</category>
    </item>
    <item>
      <title>I Boosted the Site's Performance with Better Images</title>
      <dc:creator>Tömő Viktor</dc:creator>
      <pubDate>Fri, 12 May 2023 16:01:17 +0000</pubDate>
      <link>https://dev.to/tomoviktor/i-boosted-the-sites-performance-with-better-images-3jd</link>
      <guid>https://dev.to/tomoviktor/i-boosted-the-sites-performance-with-better-images-3jd</guid>
      <description>&lt;p&gt;I optimized the images on &lt;a href="https://tomoviktor.com/"&gt;my personal blog&lt;/a&gt;. Let me tell you the goals of this series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;On my site, every blog or series has a thumbnail. These are used no matter what and loaded in for everyone. They are also present in the &lt;a href="https://tomoviktor.com//posts/meta-tags#image-and-alt-text"&gt;OG meta tags&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Because of their high usage it is essential to optimize them by creating &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images"&gt;responsive images&lt;/a&gt;. It is good for the client and the server too. The client gets a better performance (load time). The server's bandwidth will not be overloaded.&lt;/p&gt;

&lt;p&gt;Before the update I made, I had to manipulate images manually. Compress and remove metadata from them. It was really annoying. I automated this process (also made it better). In this series I will share how I've done it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Goals
&lt;/h2&gt;

&lt;p&gt;There will be two parts of the implementation. First, in order to use different sized images, they need to be generated. Second, use our new fancy images on the site. More details can be seen in each part of the series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Note
&lt;/h2&gt;

&lt;p&gt;I am using the &lt;a href="https://astro.build/"&gt;Astro web framework&lt;/a&gt;, so some parts of the series will be Astro specific. I am going to use typescript.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>astro</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
