Kitty integration to CL and gnome

Hi everyone,

I’m not sure if this fits here, but I see it as documented feedback.

I really love the default .bashrc of Clear Linux. It’s pretty standard, but having the default Git branch name and error indication is really nice.

However, I enjoy using ligatured fonts, and GNOME Terminal doesn’t support ligatures. So, I use Kitty as my terminal.

This is where my issue started. After installing Kitty, I encountered this error:

bash: command substitution: line 4: syntax error near unexpected token `)'
bash: command substitution: line 4: `      fi ;       host="${ORANGE}SOMETHING${WHITE}" ;       dir="${BLUE}~${WHITE}" ; echo "${username}@${host}${dir}$(__git_ps1) ${endchar} " )'

After checking .bashrc and the various loaded files, I traced the error back to:

/usr/share/defaults/etc/profile.d/50-prompt.sh

At first, I tried rewriting the script in a simpler way, breaking it into multiple parts to pinpoint the exact issue. This worked for a while.

I had a similar problem when integrating Kitty as the default terminal launched when using “Open in Terminal” from the right-click menu in nautilus. It took me a while to figure out, and the solution was quite convoluted.

However, after running swupd repair , my installation broke again, and I lost all my modified scripts in the process.

Instead of going through the same process again, I used the DeepSeek Deepthink-R1 LLM. I don’t really care about the using a “LLM” in this context, it’s not the kind of programming or problem-solving I enjoy when I just want to use my system for coding.

That said, the solutions generated by the model were elegant and solved all my issues in no time. Since the scripts it produced are far cleaner than what I had written, I thought it might be interesting to share them here for anyone who might want (or need) a similar solution.

So here are the scripts.

For the default prompt :
50-prompt.sh :

if [ "$(expr $- : '.*i')" -ne 0 ]; then
  if [ -e /usr/share/git-core/git-prompt.sh ]; then
    . /usr/share/git-core/git-prompt.sh
  fi

  # For bash/sh
  if [ -z "$ZSH_VERSION" ]; then
    # Define colors
    BLUE='\[\e[38;5;39m\]'
    RED='\[\e[31m\]'
    ORANGE='\[\e[38;5;208m\]'
    WHITE='\[\e[0m\]'

    PROMPT_COMMAND='__prompt_command'
    __prompt_command() {
      local EXIT="$?"
      
      # Username color
      if [ "$UID" = 0 ]; then
        username="${RED}\u${WHITE}"
        endchar_symbol="#"
      else
        username="${BLUE}\u${WHITE}"
        endchar_symbol="\\$"
      fi

      # Host and directory
      host="${ORANGE}\H${WHITE}"
      dir="${BLUE}\w${WHITE}"

      # Exit code color
      if [ "$EXIT" -ne 0 ]; then
        endchar="${RED}${endchar_symbol}${WHITE}"
      else
        endchar="${WHITE}${endchar_symbol}"
      fi

      # Git info
      if [ "$(type -t __git_ps1)" = "function" ]; then
        git_info="$(__git_ps1 " (%s)")"
      else
        git_info=""
      fi

      # Set PS1
      PS1="${username}@${host} ${dir}${git_info} ${endchar} "

      # Xterm title
      if [[ "$TERM" =~ xterm.* ]]; then
        echo -ne "\033]0;${USER}@${HOSTNAME%%.*} :: ${PWD/$HOME/~}\007"
      fi
    }
  else
    local _root_endch="%(?.#.%F{red}#%f)"
    local _other_endch="%(?.$.%F{red}$%f)"
    local _endchar="%(#.${_root_endch}.${_other_endch})"
    local _username="%F{%(#.red.39)}%n%f"
    local _host="%F{208}%m%f"
    local _dir="%F{39}%~%f"
    PS1="${_username}@${_host} ${_dir} ${_endchar} "
    
    __stateless_title() {
      [[ "$TERM" =~ xterm.* ]] && print -Pn "\e]2;%n\@%m :: %~\a"
    }
    __stateless_title
    autoload -Uz add-zsh-hook
    add-zsh-hook chpwd __stateless_title
  fi
fi

For the nautilus integration:
kitty-wrapper.py :

#!/usr/bin/env python3

# Standard imports
import sys
import subprocess
from urllib.parse import urlparse, unquote

# DBus/GLib imports
import dbus
import dbus.service
from dbus.mainloop.glib import DBusGMainLoop
from gi.repository import GLib

# Set up logging for debugging
import logging

logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s - %(levelname)s - %(message)s",
    stream=sys.stderr,
)
logger = logging.getLogger(__name__)


class KittyDBusService(dbus.service.Object):
    def __init__(self):
        try:
            # Initialize DBus service
            bus_name = dbus.service.BusName("org.gnome.Console", bus=dbus.SessionBus())
            dbus.service.Object.__init__(self, bus_name, "/org/gnome/Console")
            logger.info("Registered DBus service at org.gnome.Console")
        except Exception as e:
            logger.error(f"Failed to initialize DBus service: {str(e)}")
            sys.exit(1)

    @dbus.service.method(
        "org.freedesktop.Application", in_signature="asa{sv}", sender_keyword="sender"
    )
    def Open(self, uris, options, sender=None):
        try:
            logger.debug(f"Received Open request with URIs: {uris}")

            for uri in uris:
                if uri.startswith("file://"):
                    parsed = urlparse(uri)
                    path = unquote(parsed.path)
                    logger.info(f"Launching kitty in directory: {path}")

                    subprocess.Popen(
                        ["/usr/bin/kitty", "--directory", path],
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE,
                    )
                else:
                    logger.warning(f"Ignoring unsupported URI scheme: {uri}")

            return dbus.Array(signature="o")  # Empty array response

        except Exception as e:
            logger.error(f"Error handling Open request: {str(e)}")
            raise dbus.exceptions.DBusException(
                f"org.freedesktop.DBus.Error.Failed: {str(e)}"
            )


if __name__ == "__main__":
    try:
        # Initialize DBus main loop
        DBusGMainLoop(set_as_default=True)
        logger.info("Initialized DBus GLib main loop")

        # Create service instance
        service = KittyDBusService()
        logger.info("Service initialized. Waiting for requests...")

        # Start main loop
        loop = GLib.MainLoop()
        loop.run()

    except Exception as e:
        logger.critical(f"Fatal error: {str(e)}")
        sys.exit(1)

for the dbus service:
org.gnome.Console.service :

[D-BUS Service]
Name=org.gnome.Console
Exec=/usr/local/bin/kitty-wrapper
Comment=Kitty Terminal wrapper for GNOME Console
X-GNOME-Overrides=org.gnome.Console

And because a good install is one that I can reproduce easily (I’m thinking about you swupd repair)
The install script:
install-kitty-integration.sh :

#!/bin/bash
# Install Kitty-GNOME integration files

# Ensure script is run as root
if [ "$(id -u)" -ne 0 ]; then
    echo "Please run this script as root using sudo"
    exit 1
fi

# Get original user and display
ORIG_USER=$(logname)
DISPLAY=$(ps -u $ORIG_USER | awk '/Xorg/ {print $NF}')

# Install files as root
install -Dm644 50-prompt.sh \
    /usr/share/defaults/etc/profile.d/50-prompt.sh

install -Dm755 kitty-wrapper.py \
    /usr/local/bin/kitty-wrapper

install -Dm644 org.gnome.Console.service \
    /usr/share/dbus-1/services/org.gnome.Console.service

# Run user-specific commands as original user
sudo -u $ORIG_USER sh -c "export DISPLAY=$DISPLAY XAUTHORITY=/home/$ORIG_USER/.Xauthority; \
nautilus -q && nautilus >/dev/null 2>&1 &"

sudo -u $ORIG_USER XDG_RUNTIME_DIR=/run/user/$(id -u $ORIG_USER) \
    systemctl --user reload dbus

echo "Installation complete"
echo "You may need to:"
echo "- Wait 10-15 seconds for D-Bus services to reload"
echo "- Verify kitty is set as default terminal:"
echo "  gsettings get org.gnome.desktop.default-applications.terminal exec"

Here it is, hope that could help.

1 Like