Skip to content

Personal Documentation Posts

Mozc Japanese Input on Arch Linux (KDE + X11) — Working Setup

So I can repeat it if I ever need to reinstall.

System:

  • OS: Arch Linux
  • DE: KDE Plasma
  • Session: X11
  • Input Method Framework: fcitx5 with fcitx5-mozc

📦 Installed Packages

sudo pacman -S fcitx5 fcitx5-mozc fcitx5-configtool fcitx5-gtk fcitx5-qt

⚙️ Environment Variables (KDE-compliant)

Create file:

~/.config/environment.d/fcitx5.conf

Add:

GTK_IM_MODULE=fcitx5
QT_IM_MODULE=fcitx5
XMODIFIERS=@im=fcitx

🚀 Autostart fcitx5 on Login

Create file:

~/.config/autostart/fcitx5.desktop

Add:

[Desktop Entry]
Type=Application
Exec=fcitx5
Hidden=false
X-GNOME-Autostart-enabled=true
Name=Fcitx5
Comment=Start fcitx5 input method

🧪 Configure Input Method

Run:

fcitx5-configtool
  • Add Mozc to your input methods
  • Set your toggle key (e.g. Ctrl+Space)
  • Optionally reorder inputs

✅ Done!

Custom Mapping

keymap.txt

status	key	command
Precomposition	Ctrl 、	InputModeHiragana
Precomposition	Ctrl .	InputModeKatakana
Precomposition	Ctrl /	InputModeLatin
Precomposition	Ctrl F12	LaunchConfigDialog
Precomposition	Backspace	Revert
Precomposition	Ctrl Backspace	Undo
Precomposition	Enter	Commit
Precomposition	Space	InsertSpace
Composition	Ctrl ,	InputModeHiragana
Composition	Ctrl .	InputModeKatakana
Composition	Ctrl /	InputModeLatin
Composition	Backspace	Backspace
Composition	Delete	Delete
Composition	Enter	Commit
Composition	Space	Convert
Composition	Ctrl ,	ConvertToHiragana
Composition	Ctrl .	ConvertToFullKatakana
Composition	Ctrl /	ConvertToFullAlphanumeric
Composition	Ctrl ;	ConvertToHalfWidth
Composition	Ctrl '	ConvertToHalfAlphanumeric
Composition	ESC	Cancel
Composition	Ctrl u	Cancel
Conversion	Ctrl ,	InputModeHiragana
Conversion	Ctrl .	InputModeKatakana
Conversion	Ctrl /	InputModeLatin
Conversion	Backspace	Cancel
Conversion	Delete	Cancel
Conversion	Enter	Commit
Conversion	Space	ConvertNext
Conversion	Ctrl ,	ConvertToHiragana
Conversion	Ctrl .	ConvertToFullKatakana
Conversion	Ctrl /	ConvertToFullAlphanumeric
Conversion	Ctrl ;	ConvertToHalfWidth
Conversion	Ctrl '	ConvertToHalfAlphanumeric
Conversion	ESC	Cancel
Conversion	Ctrl [	Cancel

Arch Linux Maintenance Documentation

Generic commands for me to use.

1. Check disk usage and free space

To see how much space is used and available on each partition:

df -h

For more detailed usage on individual directories:

du -sh /*

To check specific folder usage, like your home directory:

du -sh /home/USER

2. Delete files in Trash

To clear the Trash:

rm -rf ~/.local/share/Trash/files/*

3. Remove unused packages and clean package cache

To remove orphaned packages (packages that were installed as dependencies but are no longer required):

sudo pacman -Rns $(pacman -Qdtq)

To clean the pacman package cache, removing old versions of packages:

sudo pacman -Sc

To remove all cached packages (careful with this as it removes all cached versions):

sudo pacman -Scc

4. Update the system

To update the system and all packages:

sudo pacman -Syu

5. List installed packages

To list all installed packages:

pacman -Q

To list only explicitly installed packages (packages you manually installed):

pacman -Qe

6. Check for broken packages

Sometimes, an update might break something. To check if there are any broken or incomplete packages:

sudo pacman -Qk

7. Remove unnecessary logs

Log files can accumulate over time. To remove systemd logs (be cautious, as this might remove useful logs):

sudo journalctl --vacuum-size=100M

To clear all logs, set a retention period (e.g., 1 week):

sudojournalctl --vacuum-time=1w

8. Remove unused dependencies (after uninstalling software)

When you remove software, there may be leftover dependencies that were installed with it but are no longer needed. Clean these up with:

sudo pacman -Rns $(pacman -Qdtq)

9. System Cleanup and Optimizing

You can also use debt of space in hidden configuration files or directories. This command cleans some space from orphaned packages or unneeded files (it’s safe to run occasionally but make sure to review packages first):

sudo pacman -Qdtq | sudo pacman -Rns -

10. Check your system for errors

Sometimes file systems can develop errors. You can check and repair them (if needed):

sudo fsck -A

For specific file systems (e.g., if you want to check /home):

sudo fsck /dev/sda3

11. Remove old kernels

If you’re using linux-lts or other kernel versions, they can take up a lot of space. Remove unused or older kernels:

sudo pacman -R linux-lts linux-lts-headers

12. Rebuild package database

Sometimes, the package database can become corrupt. If you face issues with package installation or updates, rebuild it:

sudo pacman -Syyu

13. Clear the font cache

To clear font cache and refresh it:

sudo fc-cache -f -v

14. Check running processes

To view the running processes and resource usage on your system:

top

Or for a more detailed view:

htop

15. Uninstall a package

If you need to remove a package completely (including dependencies that were installed with it):

sudo pacman -Rns <package_name>

16. View system logs

To see detailed logs of system activities, including errors or other logs:

sudo journalctl -xe

17. Check for available system updates

To check if there are updates without actually installing them:

sudo pacman -Qu

18. Checking and cleaning memory usage (swap)

To view swap usage:

swapon --show

To turn off swap (useful for troubleshooting or cleanup):

sudo swapoff -a

19. Free up system memory (clear cache)

To clear cached memory:

sudo sysctl vm.drop_caches=3

Managing MySQL (MariaDB) for Local Use on Arch Linux

So I can run small projects locally like journaling and stuff.

Installation

sudo pacman -S mariadb

sudo mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql

sudo systemctl enable mariadb

sudo systemctl start mariadb

sudo mysql_secure_installation

mysql -u root -p

Can’t login?

sudo mariadb

USE mysql;

ALTER USER 'root'@'localhost' IDENTIFIED VIA mysql_native_password USING PASSWORD('yourpassword');

FLUSH PRIVILEGES;

EXIT;

New User and Database

CREATE DATABASE mydb;
CREATE USER 'myuser'@'localhost' IDENTIFIED BY 'mypassword';
GRANT ALL PRIVILEGES ON mydb.* TO 'myuser'@'localhost';
FLUSH PRIVILEGES;

Markdown Table of Content

## Table of Contents

- [Introduction](#introduction)
- [Getting Started](#getting-started)
- [Features](#features)
- [Conclusion](#conclusion)

## Introduction
Some content...

## Getting Started
Some content...

## Features
Some content...

## Conclusion
Some content...

Remove stylesheet.css from epub files with Linux

Problem:

I cannot use my own font when an open/not-DRM .epub has it’s own stylesheet being forced.

I have not tested this in DRM epubs. Though most modern ones will not have the stylesheet.css forced. So it should be fine.

Solution:

Remove the file using a zip application without extracting.

Script:

Move books to your Linux machine.

Install Zip: sudo pacman -S zip

Save this script as: remove_styles.sh

#!/bin/bash

for epub in *.epub; do
    echo "Processing: $epub"
    zip -d "$epub" "*stylesheet.css"
done

Change permissions: chmod +x remove_styles.sh

Run: ./remove_styles.sh

Copy books again to your device.

Adobe Digital Editions via phone to Kobo Clara Colour

🛑 This won’t damage your Kobo, but I’ve only tested it on the Kobo Clara Colour. I’m not responsible for any issues that may arise. This does not promote piracy in any way—the goal is simply to enable the use of DRM-protected ebooks legally on a Kobo, without a computer. Use at your own discretion.

Problem:

In the Netherlands, Libby and OneDrive aren’t connected, which means I can’t send files from Libby to my Kobo. Public Dutch libraries don’t even have Libby yet—Erasmus University Library only recently adopted it. Without OneDrive integration, I’m forced to transfer library ebooks to my Kobo using a USB cable (which feels rather primitive in 2025).

Adobe Digital Editions allows you to sync between devices (specifically Android and desktop only). It’s a free but outdated app, so improvements are unlikely. While I can download files to my ADE account via the Android app, I can only add them to my Kobo using a computer—not very convenient when borrowing books while on the road.

Solution I found after many tests:

On your phone, locate the Digital Editions folder and send the epub file to your Kobo—but only after authorizing both your phone and Kobo under the same account. The Kobo authorization must be done via computer, but you only need to do it once! The first steps are straightforward, but the last step is trickier without your own server. While Dropbox isn’t an option (even with modifications), I found that Google Drive works well. Though the process is technical, I’ll explain it as simply as possible.

Steps:

Follow this guide to get started with Adobe Digital Editions. (Important: NEVER open an ebook as “Anonymous”—you won’t be able to read it anywhere else.) Follow all steps to download Adobe Digital Editions, create an account, and authorize your e-reader.

Next, download Adobe Digital Editions for Android. Log in with the same account and authorize your device.

NickelMenu

Download KoboRoot.zip and extract KoboRoot.tgz (this updated version is compatible with Google Drive). (See this GitHub thread for the details).

Connect your Kobo to the computer and open the .kobo folder. Since it’s a hidden folder, you can either type it directly in the address bar or enable “Show Hidden Files” in your file explorer.

Copy the .tgz file to this location and safely disconnect your Kobo.

It will now update the device and you should see an empty extra menu on the right corner.

Connect the device again. Now comes the technical part.

Config files

Open this file: .kobo/Kobo/Kobo eReader.conf

Find the lines under [OneStoreServices] for google and edit them as follow:

googledrive_link_account_start=https://authorize.kobo.com/{region}/{language}/linkcloudstorage/provider/google_drive

kobo_googledrive_link_account_enabled=False

Then add a new folder and file here: .adds/nm/config

There is nothing else for that file, no txt, nothing, just config and save it.

Add this menu in that file (copy/paste):

menu_item :main    :Dark Mode     :nickel_setting :toggle :dark_mode
menu_item :main    :My Articles   :nickel_open: library: pocket
menu_item :main    :Screenshots   :nickel_setting :toggle :screenshots
menu_item :main    :GoDrive       :nickel_open :library :godrive

menu_item :reader  :Dark Mode     :nickel_setting :toggle :dark_mode
menu_item :reader  :My Articles   :nickel_open: library: pocket

menu_item :library :Dark Mode     :nickel_setting :toggle :dark_mode
menu_item :library :My Articles   :nickel_open: library: pocket

You can also just add the line:

menu_item :main    :GoDrive       :nickel_open :library :godrive

I just thought you might like others.

Now all you have to do is open the menu on the Kobo and connect to your Google Drive. From your phone, you can then upload the books from the Digital Editions folder to the folder in the drive and SYNC!

Happy reading!

Bookmarklets for Snippets

javascript:(function(){ 
    let textToPaste = "MY SNIPPET"; 
    let field = document.activeElement;
    
    if (field && (field.tagName === "INPUT" || field.tagName === "TEXTAREA")) { 
        field.value += textToPaste; 
        field.dispatchEvent(new Event('input', { bubbles: true })); 
    } else { 
        navigator.clipboard.writeText(textToPaste).then(() => {
            alert("Text copied to clipboard! Paste it anywhere.");
        }).catch(err => {
            alert("Failed to copy text: " + err);
        });
    }
})();

If there’s an active text field, it pastes the text directly. Otherwise, it copies it to the clipboard so the user can manually paste it anywhere.

No alert version (you need to press CTRL+V after):

javascript:(function(){ 
    let textToPaste = "$x Vingerafdruk Controlleren "; 
    let field = document.activeElement;  

    if (field && (field.tagName === "INPUT" || field.tagName === "TEXTAREA")) { 
        field.value += textToPaste; 
        field.dispatchEvent(new Event('input', { bubbles: true })); 
    } else { 
        navigator.clipboard.writeText(textToPaste).catch(() => {}); 
    } 
})();

Selection and autopasting

javascript:(async function() {
    const snippets = [
        "string of text one",
        "string of text two",
        "string of text three",
        "string of text four"
    ];

    async function insertText(text) {
        let activeElement = document.activeElement;

        if (activeElement && (activeElement.isContentEditable || ['INPUT', 'TEXTAREA'].includes(activeElement.tagName))) {
            let start = activeElement.selectionStart;
            let end = activeElement.selectionEnd;
            let value = activeElement.value;
            activeElement.value = value.slice(0, start) + text + value.slice(end);
            activeElement.selectionStart = activeElement.selectionEnd = start + text.length;
            activeElement.focus();
        } else {
            try {
                await navigator.clipboard.writeText(text);
                const pastePermission = await navigator.permissions.query({ name: "clipboard-read" });

                if (pastePermission.state === "granted" || pastePermission.state === "prompt") {
                    let pasteText = await navigator.clipboard.readText();
                    document.execCommand('insertText', false, pasteText);
                }
            } catch (err) {
                alert("Snippet copied to clipboard! Paste manually with Ctrl+V.");
            }
        }
    }

    document.addEventListener('click', function(event) {
        event.preventDefault();

        let menu = document.createElement('div');
        menu.style.position = 'fixed';
        menu.style.left = event.clientX + 'px';
        menu.style.top = event.clientY + 'px';
        menu.style.background = '#fff';
        menu.style.border = '1px solid #333';
        menu.style.boxShadow = '2px 2px 10px rgba(0, 0, 0, 0.2)';
        menu.style.padding = '5px';
        menu.style.zIndex = '9999';
        menu.style.fontSize = '14px';
        menu.style.color = '#000';
        menu.style.minWidth = '220px';

        snippets.forEach(text => {
            let btn = document.createElement('button');
            btn.textContent = text;
            btn.style.display = 'block';
            btn.style.width = '100%';
            btn.style.textAlign = 'left';
            btn.style.border = 'none';
            btn.style.background = 'white';
            btn.style.padding = '8px';
            btn.style.cursor = 'pointer';
            btn.style.fontSize = '14px';

            btn.onmouseover = () => btn.style.background = '#ddd';
            btn.onmouseout = () => btn.style.background = 'white';
            btn.onclick = async () => {
                await insertText(text);
                document.body.removeChild(menu);
            };
            menu.appendChild(btn);
        });

        document.body.appendChild(menu);

        function removeMenu() {
            if (document.body.contains(menu)) {
                document.body.removeChild(menu);
            }
            document.removeEventListener('click', removeMenu);
            document.removeEventListener('keydown', keyClose);
        }

        function keyClose(e) {
            if (e.key === 'Escape') {
                removeMenu();
            }
        }

        setTimeout(() => {
            document.addEventListener('click', removeMenu);
            document.addEventListener('keydown', keyClose);
        }, 100);
    }, { once: true });
})();

Selection autopasting and cute:

javascript:(async function () {         const snippets = [
        "string of text one",
        "string of text two",
        "string of text three",
        "string of text four"
    ];             async function insertText(text) {                 let activeElement = document.activeElement;                 if (activeElement && (activeElement.isContentEditable || ["INPUT", "TEXTAREA"].includes(activeElement.tagName))) {                         let start = activeElement.selectionStart;                         let end = activeElement.selectionEnd;                         let value = activeElement.value;                         activeElement.value = value.slice(0, start) + text + value.slice(end);                         activeElement.selectionStart = activeElement.selectionEnd = start + text.length;                         activeElement.focus();                 } else {                         try {                                 await navigator.clipboard.writeText(text);                                 const pastePermission = await navigator.permissions.query({ name: "clipboard-read" });                                 if (pastePermission.state === "granted" || pastePermission.state === "prompt") {                                         let pasteText = await navigator.clipboard.readText();                                         document.execCommand("insertText", false, pasteText);                                 }                         } catch (err) {                                 console.error("Clipboard access denied.");                         }                 }         }             document.addEventListener("click", function (event) {                 event.preventDefault();                         let menu = document.createElement("div");                 menu.style.position = "fixed";                 menu.style.left = event.clientX + "px";                 menu.style.top = event.clientY + "px";                 menu.style.background = "rgba(255, 230, 242, 0.8)";                 menu.style.border = "2px solid #ff99cc";                 menu.style.boxShadow = "4px 4px 15px rgba(255, 105, 180, 0.4)";                 menu.style.padding = "8px";                 menu.style.zIndex = "9999";                 menu.style.fontSize = "14px";                 menu.style.color = "#ff1493";                 menu.style.minWidth = "220px";                 menu.style.borderRadius = "12px";                 menu.style.fontFamily = "Arial, sans-serif";                 menu.style.overflow = "hidden";                 menu.style.position = "absolute";                         let img = document.createElement("img");                 img.src = "https://docs.marisabel.nl/wp-content/uploads/2025/03/tumblr_06b12b250c154600c4e99f0b9c7be739_c1af3900_400.gif";                 img.style.position = "absolute";                 img.style.top = "0";                 img.style.left = "0";                 img.style.width = "100%";                 img.style.height = "100%";                 img.style.opacity = "0.4";                 img.style.borderRadius = "12px";                 img.style.zIndex = "-1";                 menu.appendChild(img);                         snippets.forEach((text) => {                         let btn = document.createElement("button");                         btn.textContent = text;                         btn.style.display = "block";                         btn.style.width = "100%";                         btn.style.textAlign = "center";                         btn.style.border = "none";                         btn.style.background = "rgba(255, 255, 255, 0.7)";                         btn.style.padding = "10px";                         btn.style.margin = "5px 0";                         btn.style.cursor = "pointer";                         btn.style.fontSize = "14px";                         btn.style.borderRadius = "8px";                         btn.style.color = "#ff69b4";                         btn.style.fontWeight = "bold";                         btn.style.transition = "background 0.3s, color 0.3s";                         btn.onmouseover = () => {                                 btn.style.background = "#ffff99";                                 btn.style.color = "#ff4500";                         };                         btn.onmouseout = () => {                                 btn.style.background = "rgba(255, 255, 255, 0.7)";                                 btn.style.color = "#ff69b4";                         };                         btn.onclick = async () => {                                 await insertText(text);                                 document.body.removeChild(menu);                         };                         menu.appendChild(btn);                 });                         document.body.appendChild(menu);                         function removeMenu() {                         if (document.body.contains(menu)) {                                 document.body.removeChild(menu);                         }                         document.removeEventListener("click", removeMenu);                         document.removeEventListener("keydown", keyClose);                 }                         function keyClose(e) {                         if (e.key === "Escape") {                                 removeMenu();                         }                 }                         setTimeout(() => {                         document.addEventListener("click", removeMenu);                         document.addEventListener("keydown", keyClose);                 }, 100);         }, { once: true }); })();

Plesk CMS Documentation

Opening FTP:

Open ports 21 and 49152-65535 ranges:

sudo firewall-cmd --permanent --add-port=49152-65535/tcp
sudo firewall-cmd --permanent --add-port=49152-65535/udp
sudo firewall-cmd --permanent --add-port=21/tcp
sudo firewall-cmd --reload

SLL Let’s Encrypt:

Always remove:

Redirect visitors from HTTP to HTTPS via a SEO friendly 301 redirect

Firewall Management

Here’s a concise list of commands for managing firewalld on AlmaLinux:


Basic Commands

sudo systemctl start firewalld
sudo systemctl stop firewalld
sudo systemctl restart firewalld
sudo systemctl enable firewalld
sudo systemctl disable firewalld
sudo firewall-cmd --state

Managing Rules

sudo firewall-cmd --permanent --add-service=<service>
sudo firewall-cmd --permanent --remove-service=<service>
sudo firewall-cmd --permanent --add-port=<port>/tcp
sudo firewall-cmd --permanent --remove-port=<port>/tcp
sudo firewall-cmd --permanent --add-port=<xxx-yyy>/tcp
sudo firewall-cmd --permanent --add-port=<xxx-yyy>/udp
sudo firewall-cmd --reload

Viewing Configuration

sudo firewall-cmd --list-all
sudo firewall-cmd --list-ports
sudo firewall-cmd --list-services
sudo firewall-cmd --get-active-zones
sudo firewall-cmd --query-service=<service>
sudo firewall-cmd --query-port=<port>/tcp

Zone Management

sudo firewall-cmd --get-zones
sudo firewall-cmd --permanent --zone=<zone> --add-service=<service>
sudo firewall-cmd --permanent --zone=<zone> --remove-service=<service>
sudo firewall-cmd --permanent --zone=<zone> --add-port=<port>/tcp
sudo firewall-cmd --permanent --zone=<zone> --remove-port=<port>/tcp

Direct Rules (Advanced)

sudo firewall-cmd --direct --add-rule ipv4 filter INPUT <priority> -p tcp --dport <port> -j ACCEPT
sudo firewall-cmd --direct --remove-rule ipv4 filter INPUT <priority> -p tcp --dport <port> -j ACCEPT
sudo firewall-cmd --direct --list-all