Hexa's Blog

Fedora, Change directory quickly with autojump

13/06/2025 @ Saigon Linux

Why do I create this post?

Damn man, I really expected that after install dnf install autojump-zsh, everything should run without any modification, I can start using j command. But no, command j not found. This post is all about installing autojump on Fedora.

Each user has their favorite shell, I am using zsh. this guide is all about setup autojump on zsh. The methodology is the same for fish or bash.

How to install

Step 1: Install dnf install autojump-zsh -y

$ sudo dnf install autojump-zsh -y
# Output
Updating and loading repositories:
Repositories loaded.
Package                                  Arch              Version           Repository        Size
Installing:
 autojump-zsh                            noarch            22.5.3-17.fc41    fedora         2.8 KiB
Installing dependencies:
 autojump                                noarch            22.5.3-17.fc41    fedora        93.2 KiB

Transaction Summary:
 Installing:         2 packages

Complete!

Step 2: Find installed file location rpm -ql autojump-zsh

$ rpm -ql autojump-zsh
# Output
/usr/share/autojump/autojump.zsh
/usr/share/zsh/site-functions/_j

Step 3: Load autojump.zsh when start terminal

I am using zsh, it means that I need modify ~/.zshrc and append source /usr/share/autojump/autojump.zsh.

It will load autojump.zsh when terminal start.

# File ~/.zshrc

source /usr/share/autojump/autojump.zsh

Good luck, have fun!

Firefox on Wayland, can't do mouse click

12/06/2025 @ Saigon Linux

Hi, I am running Firefox on Fedora 41, my display manager is wayland by default. While using Firefox, sometime, I can’t do mouse click on Firefox. This post will help you save 5 minutes.

First of all, the key point here is environment variable MOZ_ENABLE_WAYLAND=1. We need it before running firefox.

You can test by running this command. If it works, you can read futher, else, stop wasting your time reading my post.

$ export MOZ_ENABLE_WAYLAND=1
$ firefox

I assume that you have play with your firefox smoothly. Now, we will edit .desktop file. So what is it? it’s a application shortcut for GNOME. You can find it /usr/share/applications.

$ ll /usr/share/applications  | grep firefox
-rw-r--r--. 1 root root 9.4K Jun 12 11:16 org.mozilla.firefox.desktop

Now, edit /usr/share/applications/org.mozilla.firefox.desktop, add env MOZ_ENABLE_WAYLAND=1 on Exec= line. Remember that there are many places need to update.

Example:

# Before
Exec=firefox %u

# After
Exec=env MOZ_ENABLE_WAYLAND=1 firefox %u

Done, and good luck!

References:

How to fix ssh: connect to host github.com port 22: Connection timed out

09/06/2025 @ Saigon Linux

At first, let make it clear! on May 25th, vietnamese goverment started blocking telegram.

As a consequence, I have to use CloudFlare WARP to dogde it. It’s fine till I have trouble pushing/cloning my source code to github. I am pretty sure that CloudFlare did something as a middle man!

$ ssh -T git@github.com
# ssh: connect to host github.com port 22: Connection timed out

So, there are two choices:

  • [1] Turn off cloudflare’s warp client
  • [2] Change github ssh port from 22 to 443

This post is all about the second solution. Do ssh to ssh.github.com port 443 instead of github.com port 22.

If you run ssh -T -p 443 git@ssh.github.com and it works, you can continue reading this post. Else, this post is not for you cause ssh.github.com port 443 is also BLOCKED.

$ ssh -T -p 443 git@ssh.github.com
# Hi nguyenvinhlinh! You've successfully authenticated, but GitHub does not provide shell access.

If you see that message Hi nguyenvinhlinh! You've successfully authenticated, but GitHub does not provide shell access. You can alias ssh’s config and work around ssh's port 22, connection timeout error.

I will make it short! Edit file ~/.ssh/config with the following config.

Host github.com
Hostname ssh.github.com
Port 443

And test it with: ssh -T git@github.com. You should see output like:

Hi nguyenvinhlinh! You've successfully authenticated, but GitHub does not provide shell access.

Good luck!

References

How to install phx_new manually from source code?

05/06/2025 @ Saigon Elixir

Before going further, you must prepare your dev environment. I choose asdf - version manager. This is my .tool-versions

erlang 28.0
elixir 1.18.4

1. Clone the source code

In this post, I choose the latest version v1.8.0-rc.3

$ git clone --depth 1 --branch v1.8.0-rc.3 git@github.com:phoenixframework/phoenix.git

2. Go to directory phoenix/installer and build

$ cd phoenix/installer;
$ MIX_ENV=prod mix archive.build;
# Output
Compiling 11 files (.ex)
Generated phx_new app
Generated archive "phx_new-1.8.0-rc.3.ez" with MIX_ENV=prod

3. Install archive

$ mix archive.install phx_new-1.8.0-rc.3.ez
# Output
Are you sure you want to install "phx_new-1.8.0-rc.3.ez"? [Yn] Y
* creating /home/nguyenvinhlinh/.asdf/installs/elixir/1.18.4/.mix/archives/phx_new-1.8.0-rc.3


$ mix phx.new --version
# Output
Phoenix installer v1.8.0-rc.3

4. How to remove phx_new

$ mix archive.uninstall phx_new

Mkdocs, How to maximize content space width?

03/06/2025 @ Saigon etc

This post is all about maximize content space for Mkdocs sites. The main solution is to override css and set max-width: 100%;

1. Create docs/style.css

.md-grid {
    margin-left: auto;
    margin-right: auto;
    max-width: 100%;
}

2. Modify mkdocs.yml to use docs/style.css

extra_css:
  - style.css
[1] Before maximize content space/width
[1] Before maximize content space/width
[2] After maximize content space/width
[2] After maximize content space/width

Thông số dây AWG

28/05/2025 @ Saigon etc

AWG Code Max(A)   AWG Code Max(A)
10 55      
-—- -—-   -—- -—-
11 47   21 9
12 41   22 7
13 35   23 4.7
14 32   24 3.5
15 28   25 2.7
-—- -—-   -—- -—-
16 22   26 2.2
17 19   27 1.7
18 16   28 1.4
19 14   29 1.2
20 11   30 0.86

Câu hỏi và trả lời

1. Thông số Max(A) này là cột nào trong https://www.powerstream.com/Wire_Size.htm?

Tôi sử dụng cột Maximum amps for chassis wiring

2. Chassis Wiring là gì?

Chassis Wiring – Dây trong thiết bị/bo mạch (nội bộ).

Ứng dụng: Bên trong tủ điện, thiết bị điện tử, bảng mạch, bo mạch nguồn.

3. Power Transmission Wiring là gì?

Power Transmission – Truyền tải điện (ngoài trời hoặc công nghiệp)

Ứng dụng: Dây điện trong nhà, ngoài trời, hệ thống cấp điện công nghiệp, trạm biến áp.

4. Tại sao có sự khác biệt lớn đến như vậy giữa "Maximum amps for chassis wiring""Maximum amps for power transmission"?

Tóm tắt lại sau cùng là vì vấn đề sinh nhiệt.

Chassis Wiring – Dây trong thiết bị/bo mạch (nội bộ)

Mức dòng (amps) cao hơn

  • Chiều dài dây thường rất ngắn, nên điện trở tổng thể thấp ==> tổn thất điện năng và sinh nhiệt ít.
  • Lắp đặt trong môi trường có thể kiểm soát, chẳng hạn như bên trong hộp kim loại, thiết bị điện tử, nơi đã được tính toán kỹ về tản nhiệt.
  • Được thiết kế chịu tải trong thời gian ngắn, rất ngắn ==> không gây nguy cơ cháy nổ

Power Transmission – Truyền tải điện (ngoài trời hoặc công nghiệp)

Mức dòng (amps) thấp hơn do:

  • Chiều dài dây rất dài, tăng điện trở ==> tăng nhiệt ==> nguy cơ cháy hoặc hỏng cách điện.
  • Dây thường được bó lại hoặc đi qua ống, làm giảm khả năng tản nhiệt.
  • Yêu cầu độ an toàn và ổn định cao, vì đây là hệ thống truyền tải điện liên tục.
  • Có thể đi qua môi trường khắc nghiệt, nên cần giới hạn dòng để đảm bảo độ bền dây lâu dài.

References

ESP32, DoIt Devkit V1 Pinout

20/05/2025 @ Saigon ESP32

[1] ESP32, DoIt Devkit V1 PINOUT
[1] ESP32, DoIt Devkit V1 PINOUT

References

How to integrate DaisyUI - Nexus Dashboard to Phoenix Web Framework 1.8

05/05/2025 @ Saigon Elixir

This post will guide you integrate Nexus Dashboard to Phoenix Web Framework. First of all, you should buy and use Nexus Dashboard instead of craking it!

I will assume that you did download and unzip the web dashboard template, The latest version is 2.2.0.

Step 1: Go to nexus directory, find css assets.

My nexus version is 2.2.0, after unzip, you will need to go to nexus-html@2.2.0/src/styles/.

All css assets here! The whole point now is to replace phoenix app.css to nexus app.css.

The nexus app.css look like this:

/* Root Styling */
@import "./typography.css";
@import "./tailwind.css";
@import "./daisyui.css";

/* Custom Styling */
@import "./custom/animation.css";
@import "./custom/components.css";
@import "./custom/layout.css";

/* Plugin Overriding */
@import "./custom/plugins.css";

/* Pages */
@import "./custom/landing.css";

/* Plugin: Iconify (Lucide icons) */
@plugin "@iconify/tailwind4" {
    prefixes: lucide;
}

As you can see, they use import another css in relative paths. When we integrate it with phoenix, we need to take care those paths.

Step 2: Copy nexus css assets to phoenix web app

In phoenix web app, open directory /assets/css/ and create a new sub-directory named nexus_dashboard. In this assets/css/nexus_dashboard, copy nexus css into this, except app.css.

This is a directory tree after copy for assets/css/nexus_dashboard

$ tree assets/css/nexus_dashboard

assets/css/nexus_dashboard
├── custom
│   ├── animation.css
│   ├── components.css
│   ├── landing.css
│   ├── layout.css
│   └── plugins.css
├── daisyui.css
├── tailwind.css
└── typography.css

Step 3: Create nexus_app.css from nexus app.css

Now, come back the app.css, I don’t want a conflict with the existing one app.css, so I named a new main css file as nexus_app.css. In addition, due to different relative paths, I must update these with ./nexus_dashboard.

This is my content for nexus_app.css:

/* Root Styling */
@import "./nexus_dashboard/typography.css";
@import "./nexus_dashboard/tailwind.css";
@import "./nexus_dashboard/daisyui.css";

/* Custom Styling */
@import "./nexus_dashboard/custom/animation.css";
@import "./nexus_dashboard/custom/components.css";
@import "./nexus_dashboard/custom/layout.css";

/* Plugin Overriding */
@import "./nexus_dashboard/custom/plugins.css";

/* Pages */
@import "./nexus_dashboard/custom/landing.css";

/* Plugin: Iconify (Lucide icons) */
@plugin "../node_modules/@iconify/tailwind4" {
    prefixes: lucide;
}

Step 4. Modify phoenix web app, config/config.exs

Go to config/config.exs, find config for :tailwind and modify it to use nexus_app.css. For example:

config :tailwind,
  version: "4.1.4",
  mining_rig_monitor: [
    args: ~w(
      --input=css/nexus_app.css
      --output=../priv/static/assets/nexus_app.css
    ),
    cd: Path.expand("../assets", __DIR__)
  ]

Step 5. Modify assets/css/nexus_dashboard/tailwind.css

This gonna help tailwind find used css class, then it build nexus_app.css

@import "tailwindcss" source(none);
@source "../../../lib/your_phoenix_web";

Step 6. Build and debug

You gonna see a lot of missing javascrip library from now on, but I will show you how to solve it. At first, run mix tailwind _project_name.

100% you gonna see missing daisyui.js and daisyui-theme.js in assets/css/nexus_dashboard/daisyui.css. To solve it, you need to use relative path to assets/vendor/daisyui.js and assets/vendor/daisyui-theme.js. I would like to keep these file as is. When you create phoenix 1.8 project, daisyui.js and daisyui-theme.js is already there!.

if you delete these files and want it back, you can find it here: Use daisyUI with Tailwind CSS Standalone CLI

For example, this is my assets/css/nexus_dashboard/daisyui.css.

There are more relative paths need to be updated. Take care!

@plugin "../../vendor/daisyui.js" {
    exclude: rootscrollgutter;
}

@plugin "../../vendor/daisyui-theme.js" {
    name: "dark";
    color-scheme: dark;
    prefersdark: true;

In addition, you gonna see a mission iconify/tailwind4 library. At this point, there is no choice but using npm ecosystem. In assets, run npm install @iconify-json/lucide and @iconify/tailwind4.

This is content for assets/package.json. I really want to depend as less as possible the npm ecosystem, but have no choice.

{
  "dependencies": {
    "@iconify-json/lucide": "^1.2.39",
    "@iconify/tailwind4": "^1.0.6"
  }
}

At this point, you should be able to run mix tailwind phoenix_app_name to build your nexus_app.css.

$ mix tailwind mining_rig_monitor
/*! 🌼 daisyUI 5.0.28 */
≈ tailwindcss v4.1.4

Done in 286ms

A minor note for you, cause you need lucide-icon and rely on npm install to collect @iconify, lucide and tailwind4 for lucide-icon. it’s a good idea that you can ignore prebuild vendor/daisyui.js and daisyui-theme.js, just use npm install these two libraries. Remember to update your nexus_dashboard/daisyui.css’s relative paths.

How to install gnome extension manually?

01/05/2025 @ Saigon Linux

if you want to install automatically with gnome-browser-connector, you can visit this guide: https://gnome.pages.gitlab.gnome.org/gnome-browser-integration/pages/installation-guide.html

Step 1: Go to https://extensions.gnome.org, find and download extension in zip file.

In this example, I would like to install AppIndicator and KStatusNotifierItem Support by 3v1n0

Step 2: Extract the zip file, and find a file named metadata.json

This is file content of metadata.json.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "_generated": "Generated by SweetTooth, do not edit",
  "description": "Adds AppIndicator, KStatusNotifierItem and legacy Tray icons support to the Shell",
  "gettext-domain": "AppIndicatorExtension",
  "name": "AppIndicator and KStatusNotifierItem Support",
  "settings-schema": "org.gnome.shell.extensions.appindicator",
  "shell-version": [
    "45",
    "46",
    "47"
  ],
  "url": "https://github.com/ubuntu/gnome-shell-extension-appindicator",
  "uuid": "appindicatorsupport@rgcjonas.gmail.com",
  "version": 59
}

Look at line 13, looking for the uuid, it’s appindicatorsupport@rgcjonas.gmail.com.

Step 3: Rename the extracted directory to the uuid that is copied in Step 2.

It’s appindicatorsupport@rgcjonas.gmail.com

Step 4: Copy the renamed directory in Step-3 to ~/.local/share/gnome-shell/extensions.


How to test?

You can go to terminal and type gnome-extensions list.

$ gnome-extensions list

appindicatorsupport@rgcjonas.gmail.com
apps-menu@gnome-shell-extensions.gcampax.github.com
background-logo@fedorahosted.org
launch-new-instance@gnome-shell-extensions.gcampax.github.com
places-menu@gnome-shell-extensions.gcampax.github.com
window-list@gnome-shell-extensions.gcampax.github.com

On the other hand, you can open gnome extensions - https://apps.gnome.org/Extensions/. Your new extension should be there in Manually installed list.

[1] Gnome Extensions
[1] Gnome Extensions

Nhật ký migrate Phoenix Web Framework 1.7.14 lên 1.8

22/04/2025 Elixir

Bài viết này liệt kê lại quá trình cũng như dòng suy nghĩ của tôi khi migrate phoenix 1.7 lên 1.8.

Thay đổi mix.exs phần dependencíe deps/0:

  • {:phoenix, "~> 1.7.14"} -> {:phoenix, "1.8.0-rc.1", override: true}
  • {:phoenix_live_view, "~> 1.0.0", override: true} -> {:phoenix_live_view, "~> 1.0.9"}

Thay đổi mining_rig_monitor_web.ex , phần live_view/0:

  • use Phoenix.LiveView, layout: {MiningRigMonitorWeb.Layouts, :app} -> use Phoenix.LiveView

Sau cùng, nó sẽ trông như thế này.

  # Phiên bản cũ
  def live_view do
    quote do
      use Phoenix.LiveView,
        layout: {MiningRigMonitorWeb.Layouts, :app}

      unquote(html_helpers())
    end
  end
  # Phiên bản mới
  def live_view do
    quote do
      use Phoenix.LiveView

      unquote(html_helpers())
    end
  end

Tôi đã nghĩ rằng sẽ phải có lỗi xảy ra ở lần chạy iex -S mix phx.server đầu tiên, tôi sẽ cần phải thay đổi các live_view liên quan, nhưng mà không, khả năng tương thích ngược là khá tốt.


Tôi trích dẫn một cái quan trọng liên quan đến root.html.heexapp.html.heex (version 1.7) , app/1 (version 1.8).

defmodule DevAppWeb.Layouts do
  @moduledoc """
  This module holds different layouts used by your application.

  See the `layouts` directory for all templates available.
  The "root" layout is a skeleton rendered as part of the
  application router. The "app" layout is rendered as component
  in regular views and live views.
  """
end

cái app/1 này phải được gọi thì nó mới render, không có chuyện chạy mặc định. Có thể ở 1.7 có file app.html.heex, tuy nhiên, nó ko liên quan đến func app/1

Tôi cần kiểm tra chéo 1 chút, thực sự là app.html.heex có được chạy mặc định hay không, sau khi tôi đã bỏ option layout: {MiningRigMonitorWeb.Layouts, :app}

Để test, tôi đã thêm 1 cái tag <h1> cho file app.html.heex để đánh dấu.

 <!-- app.html.heex -->
<main class="bg-gray-50 dark:bg-gray-900">
  <.flash_group flash={@flash} />
  <%= @inner_content %>
  <h1> APP.HTML.HEEX</h1>
</main>

Đệt, nó ko chạy qua app.html.heex sau khi bỏ option layout: {MiningRigMonitorWeb.Layouts, :app} trong _web.ex, live_view/0 nhé.

Điều này nghĩa là tôi sẽ phải:

  • Kéo nội dung của app.html.heex vào module Layouts, function app
  • Thêm alias MiningRigMonitorWeb.Layouts trong mining_rig_monitor_web.ex, function html_heler/0
  • Các liveview module, ví dụ module MiningRigMonitorWeb.AsicMinerLive.Index, sau khi tôi tách function render/1 thành file index.html.heex. nếu tôi mà muốn sử dụng liveview layout có tên là app, tôi sẽ cần phải bọc nó lại với tag <Layout.app> <‌/Layout.app>

Ví dụ file mining_rig_monitor/lib/mining_rig_monitor_web/live/asic_miner_live/index.html.heex

<Layouts.app flash={@flash} >
  <._index_top />

  <._index_overall_figures aggregated_coin_hashrate_map={@aggregated_coin_hashrate_map}
    aggregated_total_power={@aggregated_total_power}
    aggregated_total_power_uom={@aggregated_total_power_uom}
    aggregated_asic_miner_alive={@aggregated_asic_miner_alive} />

  <._index_activated_asic_miner_table streams={@streams} />

  <._index_not_activated_asic_miner_table streams={@streams} />

</Layouts.app>

Tiếp theo là về cái vụ Scope. Tôi tính toán là sẽ mix phx.gen.auth ở một dự án test khác, sau đó sẽ copy quá. Tuy nhiên, bị vướng field :authenticated_at, :utc_datetime trong Accounts.UserToken, ở phiên bản 1.7, không có field authenticated_at.

Tôi có xem kỹ hơn cái field này, có vẻ là nó liên quan đến sudo mode. Thế tính năng sudo mode là gì?

Tính năng cực kỳ thích hợp cho những tác vụ nhạy cảm. Nó sẽ yêu cầu người dùng phải đăng nhập lại trước khi đưa ra hành động nào đó.

Tôi không có nhu cầu dùng sudo mode. Cụ thể là trong dự án Mining Rig Monitor. Thực tế, app này chỉ có 1 user role là admin. Chả có nhu cầu đụng đến Scope luôn, thôi dẹp cho khỏe.

Hahaha