Sign in

Steffen Deusch

@steffend.me
323 followers 75 following 167 posts

Software Developer (mostly Elixir). Mainly here to read. Fediverse social.deusch.me/@steffen

PostsRepliesMedia
Steffen Deusch @steffend.me · 07/09/2026
btw nice to see a privacy policy on the site now, note that it does not render nicely on mobile at the moment
100
Steffen Deusch @steffend.me · 26/05/2026
"Hot" take: in Germany landlords are required to ensure proper heating, but I‘d say proper cooling should be mandatory as well. One problem is that many Germans are too stubborn to accept air conditioning.
Screenshot of HomeAssistant temperature readings. All rooms are 28°C+.
120
Steffen Deusch @steffend.me · 29/04/2026
Thanks to everyone that joined me for my first ever conference talk last Thursday at @elixirconf.bsky.social. It was a lot of fun! Remember to try the #Phoenix #LiveView 1.2.0 release candidate and report any issues. I'm also happy to hear if everything works just fine for you :)
Me, a white male guy, standing on stage at ElixirConf EU Malaga 2026. I'm wearing a black Kurzgesagt zipper hoodie, a Phoenix T-Shirt and a blue jeans. ElixirConf graphics hanging behind me, my laptop in front of me, people sitting in the front, bottom part of the image. On the top right, the projected slides show "Update from the Phoenix team".A close up of me holding the slide remote in my right hand, gesturing with my right hand.Me on stage, again gesturing with my right hand.Me on stage, holding my hands together, probably answering a question. The track host stands right next to me, to the right.
1202
Steffen Deusch @steffend.me · 09/03/2026
Since I have a fork of Chrome anyway now, another thing I changed is that external links in those web apps end up being opened by the system default browser instead. I use the Zen browser as default and only run Chromium for its web app integration. Links in Slack now properly open in Zen :)
A small git patch with content:

diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc
index afd65467c7..d4ee2a6f5c 100644
--- a/chrome/browser/ui/browser_navigator.cc
+++ b/chrome/browser/ui/browser_navigator.cc
@@ -657,6 +657,17 @@ base::WeakPtr<content::NavigationHandle> Navigate(NavigateParams* params) {
     params->browser = override_params->browser();
     singleton_index = override_params->tab_index().value_or(-1);
   } else {
+    // If the navigation originates from a web app window and the URL is outside
+    // the app's scope, open it in the OS default browser via xdg-open (or
+    // platform equivalent) instead of in a Chromium window.
+    if (source_browser && source_browser->is_type_app() &&
+        source_browser->app_controller() &&
+        !source_browser->app_controller()->IsUrlInAppScope(params->url) &&
+        params->url.SchemeIsHTTPOrHTTPS()) {
+      platform_util::OpenExternal(params->url);
+      return nullptr;
+    }
+
     std::tuple<BrowserWindowInterface*, int> browser_and_index =
         GetBrowserAndTabForDisposition(*params);
     params->browser =
100
Steffen Deusch @steffend.me · 09/03/2026
So now I have a 600 line patch for Chromium, but I have absolutely now experience in contributing to Chromium and since it's LLM-generated code, I don't have C++ experience, it doesn't have any tests, and only benefits Linux+Pulseaudio, it's quite unlikely that it ends up in Chrome.
A git diff --stat, showing  41 files changed, 593 insertions(+), 56 deletions(-) and details for each of the 41 files.
100
Steffen Deusch @steffend.me · 09/03/2026
So now I have a local fork of ungoogled-chromium that actually sets the correct pulseaudio properties for each audio stream. It dynamically updates the stream label when the document title changes and it ensures that the correct app information for web apps is set.
Fedora audio volume manager showing two entries. One for "Apple Music" and one for "Discord".
110
Steffen Deusch @steffend.me · 09/03/2026
Since switching to Linux on my MacBook, I've been running Discord and some other apps as Chrome Web apps. It's nice that you can easily wrap any page into its own launcher icon. What's not so nice is that all apps' audio streams just end up as "Chromium" in the system.
Fedora audio volume manager showing an entry called "Chromium"
150
Steffen Deusch @steffend.me · 03/03/2026
„You must be 18 or older to use“ your text editor. What the. #zed
Zed (text editor) informing that they’re changing their terms of service. The message states that starting March 2nd (yesterday) „you must be 18 or older to use Zed“.
380
Steffen Deusch @steffend.me · 12/02/2026
At the beginning of February I used a Sunday afternoon to make some space on my MacBook's internal drive to install #Asahi Linux as a daily driver. Apart from some minor annoyances it's working quite well.
The KDE "About this System" page showing Asahi Linux running on my MacBook Pro M1 Max.
190
Steffen Deusch @steffend.me · 07/06/2025
One way to improve that right now is to use LiveComponents. Basically, instead of directly rendering the content of the tag given to `:for`, you render a LiveComponent instead, which does perform change-tracking on its own assigns. This is cumbersome though, as you need to define a separate module.
A HEEx template with code:

~H"""
<ul>
  <.live_component
    :for={%{id: id, name: name} <- @items}
    module={MyListComponent}
    id={id}
    name={name}
  />
</ul>
"""
220
Steffen Deusch @steffend.me · 07/06/2025
One common pitfall when using lists in #LiveView is that they are not change-tracked. We might have something to improve that situation in an upcoming update! #MyElixirStatus #Phoenix #ElixirLang
A HEEx template:

~H"""
<ul>
  <li :for={%{id: id, name: name} <- @items} :key={@id}>
    Count: <span>{@count}</span>,
    item: {name}
  </li>
</ul>
"""

Notice the special `:key` attribute.
2567
Steffen Deusch @steffend.me · 30/05/2025
In the past you'd need to either add a hook or a global `onBeforeElUpdated` dom option when defining your LiveSocket. Both would be really cumbersome for a library. The hook part is easier now with ColocatedHooks, but with `JS.ignore_attributes` it's even easier!
HTML Code:

<details phx-mounted={JS.ignore_attributes(["open"])}>
  <summary>...</summary>
  ...
</details>
140
Steffen Deusch @steffend.me · 30/05/2025
I recently showed one example of a macro component that renders markdown at compile time: bsky.app/profile/stef... The code to do syntax highlighting is not much more complex.
An Elixir module with code:

  defmodule SyntaxHighlight do
    @behaviour Phoenix.Component.MacroComponent

    @impl true
    def transform({"pre", attrs, children, _tag_meta}, _meta) do
      code = Phoenix.Component.MacroComponent.ast_to_string(children)
      lang = Map.new(attrs)["language"] || raise ArgumentError, "language attribute is required"
      html_doc = highlight(String.trim_leading(code), lang)

      stylesheet =
        Makeup.Styles.HTML.Style.stylesheet(Makeup.Styles.HTML.StyleMap.monokai_style())

      {:ok,
       {"pre", [{"class", "highlight"} | attrs],
        [
          {"style", [], [stylesheet, ".highlight { padding: 8px; border-radius: 4px; }"], %{}},
          html_doc
        ], %{}}}
    end

    defp highlight(code, lang) do
      Application.ensure_all_started([:makeup, :makeup_elixir, :makeup_eex, :makeup_syntect])

      case Makeup.Registry.get_lexer_by_name(lang) do
        {lexer, opts} ->
          Makeup.highlight_inner_html(code, lexer: lexer, lexer_options: opts)

        _ ->
          code
      end
    end
  endA Phoenix component with code

  defp lv_code_sample(assigns) do
    ~H'''
    <pre :type={SyntaxHighlight} language="elixir" phx-no-curly-interpolation>
    defmodule MyAppWeb.ThermostatLive do
      use MyAppWeb, :live_view

      def render(assigns) do
        ~H"""
        Current temperature: {@temperature}°F
        <button phx-click="inc_temperature">+</button>
        """
      end

      def mount(_params, _session, socket) do
        temperature = 70 # Let's assume a fixed temperature for now
        {:ok, assign(socket, :temperature, temperature)}
      end

      def handle_event("inc_temperature", _params, socket) do
        {:noreply, update(socket, :temperature, &(&1 + 1))}
      end
    end
    </pre>
    '''
  endA webpage showing the syntax highlighted output next to a chrome devtools tab with the HTML showing the highlighting classes generated by the Elixir Makeup library.
2130
Steffen Deusch @steffend.me · 30/05/2025
Macro components are a new, special kind of component in HEEx that works fully at compile time. It receives an HTML-AST representation of the node it is defined on and can transform it into something else. And that something else can also be nothing, removing a script and write it to a file.
HTML code of a HEEx template showing an `<input>` tag with `phx-hook` attribute. Directly below is a `<script>` tag with special `:type` attribute set to `{Phoenix.LiveView.ColocatedHook}`.a typical app.js from a LiveView application, showing a special import:

import { hooks as colocatedHooks } from "phoenix-colocated/colocated_demo";

This import is then spread into the hooks parameter of the liveSocket:

hooks: { ...colocatedHooks, ...hooks }
1160
Steffen Deusch @steffend.me · 30/05/2025
1. Type definitions for public JavaScript interfaces. I know that many people enjoy that you don't need to write much JavaScript when developing a LiveView application, but you cannot always avoid it entirely. We now have official type declarations shipped with the LiveView JS!
An editor window with a LiveView hooks object declaration:

const hooks = {
  PhoneNumber: {
    mounted() {
      this.
    }
  }
}

The cursor position is currently after the dot of `this.` and the editor is suggesting all the available hook properties and methods like `beforeUpdate`, which is currently selected and shows a description of the beforeUpdate method on the right.An editor window where a LiveView hook is defined. Currently, the cursor position is inside a `mounted` method, the unfinished code snippet reading `this.js().`. The editor is suggesting all the available client-side JS commands. Currently selected is `addClass` with a description of the available parameters.An editor window defining a new LiveView LiveSocket instance. IntelliSense also works on the liveSocket declaration, currently suggesting the available methods in the `dom` property of the LiveSocket parameters.
2212
Steffen Deusch @steffend.me · 23/05/2025
While working on Colocated Hooks in LiveView, we also found some other cool things you can do, such as rendering markdown at compile time 👀 #MyElixirStatus #ElixirLang #PhoenixLiveView
An Elixir module with contents:

defmodule ColocatedDemoWeb.Markdown do
  @behaviour Phoenix.Component.MacroComponent

  @impl true
  def transform({"pre", attrs, children}, _meta) do
    markdown = Phoenix.Component.MacroComponent.AST.to_string(children)
    {:ok, html_doc, _} = Earmark.as_html(markdown)

    {"div", attrs, [html_doc]}
  end
endA LiveView render function with contents:

  def render(assigns) do
    ~H"""
    <pre :type={ColocatedDemoWeb.Markdown} class="prose mt-8">
    ## Hello World

    This is some markdown!

    ```elixir
    defmodule Hello do
      def world do
        IO.puts "Hello, world!"
      end
    end
    ```

    ```html
    <h2>Hey</h2>
    ```
    </pre>
    """
  endA webpage with the rendered markdown content.
25312