feat: enhance Linux support with ydotool integration and improve logging

This commit is contained in:
Your Name
2026-06-09 00:25:05 -07:00
parent bc3d8cbce3
commit 05c8fbc474
7 changed files with 421 additions and 118 deletions
+235 -18
View File
@@ -33,7 +33,7 @@ fi
if [ -z "$APP_VERSION" ]; then
APP_VERSION="dev"
fi
echo "[0/3] App version: $APP_VERSION"
echo "[0/4] App version: $APP_VERSION"
echo ""
# Function to check if a command exists
@@ -54,7 +54,7 @@ if ! command_exists wails; then
fi
# 2. Check for Linux dependencies
echo "[1/3] Checking system dependencies..."
echo "[1/4] Checking system dependencies..."
MISSING_DEPS=0
# 2a. GNOME tray icon support check (before build — avoids launching app if this will fail)
@@ -79,7 +79,7 @@ if [ "$XDG_CURRENT_DESKTOP" = "GNOME" ] || [ "$XDG_CURRENT_DESKTOP" = "ubuntu:GN
if [ "$EXT_INSTALLED" = false ]; then
echo ""
echo "==============================================================="
echo " ERROR: GNOME AppIndicator extension is not installed"
echo " WARNING: GNOME AppIndicator extension is not installed"
echo "==============================================================="
echo ""
echo " GNOME does not show system tray icons without this extension."
@@ -104,7 +104,10 @@ if [ "$XDG_CURRENT_DESKTOP" = "GNOME" ] || [ "$XDG_CURRENT_DESKTOP" = "ubuntu:GN
echo " Settings > Extensions > AppIndicator and KStatusNotifierItem Support"
echo ""
echo "==============================================================="
exit 1
# Do NOT exit — just warn and continue. The user can still use the app
# with a custom shortcut even without the tray icon.
echo " Continuing (the app will work, but the tray icon may be hidden)."
echo ""
else
echo "[INFO] GNOME AppIndicator extension is installed."
echo " Make sure it's enabled in Settings > Extensions."
@@ -120,6 +123,39 @@ for dep in "${DEPS[@]}"; do
fi
done
# ydotool check for text injection
if ! command_exists ydotool; then
echo ""
echo "==============================================================="
echo " WARNING: ydotool is not installed"
echo "==============================================================="
echo ""
echo " ydotool is required for automatic text injection (paste) on"
echo " Wayland. Without it, transcribed text will only be copied to"
echo " your clipboard."
echo ""
echo " Install ydotool and configure it:"
echo ""
if command_exists dnf; then
echo " sudo dnf install ydotool"
elif command_exists apt-get; then
echo " sudo apt install ydotool"
elif command_exists pacman; then
echo " sudo pacman -S ydotool"
else
echo " Install ydotool from your distribution's package manager."
fi
echo ""
echo " Then set up udev rules for /dev/uinput access:"
echo " echo 'KERNEL==\"uinput\", SUBSYSTEM==\"misc\", TAG+=\"uaccess\", OPTIONS+=\"static_node=uinput\"' |"
echo " sudo tee /etc/udev/rules.d/80-uinput.rules"
echo " sudo udevadm control --reload-rules && sudo udevadm trigger"
echo ""
echo " Then enable the ydotool user service (see the --install-systemd flag below)."
echo "==============================================================="
echo ""
fi
# We can't easily check C headers, but we can try to find them with pkg-config
# (Fedora often ships webkit2gtk-4.1.pc; Debian/Ubuntu often use webkit2gtk-4.0.pc)
webkit2_ok() {
@@ -160,15 +196,15 @@ if [ $MISSING_DEPS -eq 1 ]; then
DEBIAN_DEPS="build-essential pkg-config libgtk-3-dev libwebkit2gtk-4.0-dev libasound2-dev libayatana-appindicator3-dev"
# Runtime nicety (optional): playerctl pauses media while recording
DEBIAN_RUNTIME_OPT="playerctl"
DEBIAN_RUNTIME_OPT="playerctl ydotool"
# Fedora 40+: WebKit2GTK 4.0 packages are gone; use 4.1 + Wails -tags webkit2_41 (see wails build below).
# pkgconf-pkg-config provides `pkg-config` on Fedora.
FEDORA_DEPS="gcc gcc-c++ make pkgconf-pkg-config gtk3-devel webkit2gtk4.1-devel alsa-lib-devel libayatana-appindicator-gtk3-devel"
# Same as FEDORA_DEPS but classic libappindicator (some spins/repos lack Ayatana -devel)
FEDORA_DEPS_ALT="gcc gcc-c++ make pkgconf-pkg-config gtk3-devel webkit2gtk4.1-devel alsa-lib-devel libappindicator-gtk3-devel"
FEDORA_RUNTIME_OPT="playerctl xdg-desktop-portal"
FEDORA_RUNTIME_OPT="playerctl xdg-desktop-portal ydotool"
ARCH_DEPS="base-devel pkgconf gtk3 webkit2gtk alsa-lib libayatana-appindicator"
ARCH_RUNTIME_OPT="playerctl"
ARCH_RUNTIME_OPT="playerctl ydotool"
echo "The full list of dependencies needed:"
echo " [Ubuntu/Debian]: sudo apt update && sudo apt install -y $DEBIAN_DEPS"
@@ -207,7 +243,7 @@ if [ $MISSING_DEPS -eq 1 ]; then
fi
# 3. Build the application
echo "[2/3] Building with Wails..."
echo "[2/4] Building with Wails..."
# Pre-emptively fix npm bin permissions if they got messed up (common issue on some systems)
if [ -d "frontend/node_modules/.bin" ]; then
@@ -253,28 +289,153 @@ fi
# doesn't block the terminal while we ask about installation.
pkill -f "$EXECUTABLE" 2>/dev/null || true
echo "[3/3] Build successful!"
echo "[3/4] Build successful!"
echo " Output: $EXECUTABLE"
echo ""
# 4. Optional Installation
read -p "Would you like to install it globally to /usr/local/bin and add a desktop shortcut? (y/n): " INSTALL
if [[ "$INSTALL" == "y" || "$INSTALL" == "Y" ]]; then
echo "Installing..."
echo "[4/4] Installation options"
echo ""
echo " (i) Install systemwide: sudo ./$0 --install"
echo " (u) Install user-local: ./$0 --install-user"
echo " (s) Install systemd service (ydotool): ./$0 --install-systemd"
echo " (h) Show this help"
echo ""
echo " Without flags, the build-only mode finishes here."
echo " Binary is ready at: $EXECUTABLE"
echo ""
# Parse flags
INSTALL_MODE="none" # none, system, user
INSTALL_SYSTEMD=false
for arg in "$@"; do
case "$arg" in
--install)
INSTALL_MODE="system"
;;
--install-user)
INSTALL_MODE="user"
;;
--install-systemd)
INSTALL_SYSTEMD=true
;;
--help|-h)
echo "Usage: $0 [--install|--install-user|--install-systemd|--help]"
echo ""
echo " (no flags) Build only — outputs binary to $EXECUTABLE"
echo " --install Build + install system-wide (/usr/local/bin)"
echo " --install-user Build + install per-user (~/.local/bin)"
echo " --install-systemd Generate and enable systemd user units for ydotool"
echo " --help Show this message"
echo ""
exit 0
;;
esac
done
# If no install flags were passed, do interactive prompt (backward-compatible)
if [ "$INSTALL_MODE" = "none" ] && [ "$INSTALL_SYSTEMD" = false ]; then
read -p "Would you like to install it system-wide to /usr/local/bin? (y/n): " INSTALL
if [[ "$INSTALL" == "y" || "$INSTALL" == "Y" ]]; then
INSTALL_MODE="system"
else
read -p "Install per-user to ~/.local/bin? (y/n): " INSTALL_USER
if [[ "$INSTALL_USER" == "y" || "$INSTALL_USER" == "Y" ]]; then
INSTALL_MODE="user"
fi
fi
read -p "Would you like to set up the ydotool systemd user service? (y/n): " SETUP_SYSTEMD
if [[ "$SETUP_SYSTEMD" == "y" || "$SETUP_SYSTEMD" == "Y" ]]; then
INSTALL_SYSTEMD=true
fi
fi
# --- Systemd ydotool service setup ---
if [ "$INSTALL_SYSTEMD" = true ]; then
echo ""
echo "========================================"
echo " Setting up ydotool systemd user service"
echo "========================================"
echo ""
SYSTEMD_USER_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/systemd/user"
mkdir -p "$SYSTEMD_USER_DIR"
# Create ydotool.service for the user session
cat > "$SYSTEMD_USER_DIR/ydotool.service" << 'YDSVCEOF'
[Unit]
Description=ydotool daemon — simulate keyboard input on Wayland
Documentation=man:ydotool(1)
[Service]
Type=simple
ExecStart=/usr/bin/ydotoold
Restart=on-failure
RestartSec=2
[Install]
WantedBy=default.target
YDSVCEOF
echo "[INFO] Created $SYSTEMD_USER_DIR/ydotool.service"
# Check/create udev rule for uinput
UDEV_RULE_FILE="/etc/udev/rules.d/80-uinput.rules"
if [ ! -f "$UDEV_RULE_FILE" ]; then
echo ""
echo " /dev/uinput permission rule not found."
echo " The following rule is needed for ydotool to inject keystrokes:"
echo ""
echo ' KERNEL=="uinput", SUBSYSTEM=="misc", TAG+="uaccess", OPTIONS+="static_node=uinput"'
echo ""
read -p " Create $UDEV_RULE_FILE now? (requires sudo) (y/N): " CREATE_UDEV
if [[ "$CREATE_UDEV" == "y" || "$CREATE_UDEV" == "Y" ]]; then
echo 'KERNEL=="uinput", SUBSYSTEM=="misc", TAG+="uaccess", OPTIONS+="static_node=uinput"' | \
sudo tee "$UDEV_RULE_FILE" > /dev/null
sudo udevadm control --reload-rules && sudo udevadm trigger
echo "[INFO] udev rule created and reloaded."
echo " You must log out and back in (or reboot) for the permissions to take effect."
else
echo " Skipping udev rule creation. ydotoold may not be able to inject keystrokes."
echo " You can create it manually later."
fi
else
echo "[INFO] udev rule already exists at $UDEV_RULE_FILE"
fi
# Reload systemd user daemon and enable the service
echo ""
echo " Reloading systemd user daemon..."
systemctl --user daemon-reload
echo " Enabling ydotool user service..."
systemctl --user enable ydotool.service
echo " Starting ydotool user service..."
systemctl --user start ydotool.service || echo " [WARN] Could not start (may need logout/login for udev perms)"
echo ""
echo " ydotool systemd service is now set up."
echo " You can check its status with: systemctl --user status ydotool"
echo ""
fi
# --- Binary installation ---
if [ "$INSTALL_MODE" = "system" ]; then
echo "Installing system-wide..."
# Needs sudo
sudo cp "$EXECUTABLE" "/usr/local/bin/$APP_NAME"
sudo chmod +x "/usr/local/bin/$APP_NAME"
# Create Desktop shortcut
DESKTOP_FILE="/usr/share/applications/$APP_NAME.desktop"
# Try to grab the icon from the Wails build directory if available
ICON_PATH="/usr/share/pixmaps/$APP_NAME.png"
if [ -f "build/appicon.png" ]; then
sudo cp "build/appicon.png" "$ICON_PATH"
fi
# Absolute Exec path: some desktop environments do not put /usr/local/bin on PATH
# for .desktop launches, so "Exec=wis-free-v3" can fail with no visible error.
cat << EOF > /tmp/$APP_NAME.desktop
@@ -292,10 +453,66 @@ EOF
sudo mv /tmp/$APP_NAME.desktop "$DESKTOP_FILE"
sudo chmod 644 "$DESKTOP_FILE"
echo ""
echo "Installation complete! You can now launch 'WIS Free V3' from your app launcher,"
echo "or by typing '$APP_NAME' in your terminal."
else
echo ""
elif [ "$INSTALL_MODE" = "user" ]; then
echo "Installing per-user..."
LOCAL_BIN_DIR="$HOME/.local/bin"
mkdir -p "$LOCAL_BIN_DIR"
cp "$EXECUTABLE" "$LOCAL_BIN_DIR/$APP_NAME"
chmod +x "$LOCAL_BIN_DIR/$APP_NAME"
# Add to PATH warning
case ":${PATH}:" in
*:"$LOCAL_BIN_DIR":*) ;;
*)
echo "[WARNING] $LOCAL_BIN_DIR is not in your PATH."
echo " Add it to your shell profile:"
echo ' export PATH="$HOME/.local/bin:$PATH"'
echo ""
;;
esac
# Create user-local desktop file
DESKTOP_DIR="$HOME/.local/share/applications"
mkdir -p "$DESKTOP_DIR"
ICONS_DIR="$HOME/.local/share/pixmaps"
mkdir -p "$ICONS_DIR"
if [ -f "build/appicon.png" ]; then
cp "build/appicon.png" "$ICONS_DIR/$APP_NAME.png"
fi
cat > "$DESKTOP_DIR/$APP_NAME.desktop" << EOF
[Desktop Entry]
Type=Application
Name=WIS Free V3
Comment=Voice Dictation App
Exec=$LOCAL_BIN_DIR/$APP_NAME
TryExec=$LOCAL_BIN_DIR/$APP_NAME
Icon=$APP_NAME
StartupWMClass=$APP_NAME
Terminal=false
Categories=Utility;Audio;
EOF
chmod 644 "$DESKTOP_DIR/$APP_NAME.desktop"
echo ""
echo "User-local installation complete!"
echo " Binary: $LOCAL_BIN_DIR/$APP_NAME"
echo " Desktop: $DESKTOP_DIR/$APP_NAME.desktop"
echo ""
elif [ "$INSTALL_MODE" = "none" ] && [ "$INSTALL_SYSTEMD" = false ]; then
echo "Skipping installation. You can run the app directly via: ./$EXECUTABLE"
fi
echo "========================================"
echo " Build finished successfully!"
echo "========================================"
+11 -1
View File
@@ -80,9 +80,15 @@ if ! pkg-config --exists gtk+-3.0 || ! webkit2_ok || ! pkg-config --exists alsa
echo ""
echo "Ubuntu/Debian:"
echo " sudo apt update && sudo apt install -y build-essential pkg-config libgtk-3-dev libwebkit2gtk-4.0-dev libasound2-dev libayatana-appindicator3-dev dpkg-dev rpm"
echo " sudo apt install -y ydotool (recommended for automatic text injection)"
echo ""
echo "Fedora:"
echo " sudo dnf install -y gcc gcc-c++ make pkgconf-pkg-config gtk3-devel webkit2gtk4.1-devel alsa-lib-devel libayatana-appindicator-gtk3-devel rpm-build"
echo " sudo dnf install -y ydotool (recommended for automatic text injection)"
echo ""
echo "Arch Linux:"
echo " sudo pacman -S base-devel pkgconf gtk3 webkit2gtk alsa-lib libayatana-appindicator"
echo " sudo pacman -S ydotool (recommended for automatic text injection)"
echo ""
exit 1
fi
@@ -185,8 +191,10 @@ Architecture: $ARCH_DEB
Maintainer: $MAINTAINER
Installed-Size: $INSTALLED_SIZE
Depends: libgtk-3-0, libwebkit2gtk-4.0-37 | libwebkit2gtk-4.1-0, libasound2, libayatana-appindicator3-1
Recommends: ydotool
Description: $COMMENT
WIS Free V3 is a desktop voice dictation app built with Go and Wails.
On Wayland, install ydotool for automatic text injection.
EOF
dpkg-deb --build "$DEB_ROOT" "$DIST_DIR/${APP_NAME}_${PKG_VERSION}-${PKG_RELEASE}_${ARCH_DEB}.deb"
@@ -206,9 +214,11 @@ Summary: $COMMENT
License: $LICENSE
Requires: gtk3
Requires: alsa-lib
Requires: libayatana-appindicator-gtk3
%description
WIS Free V3 is a desktop voice dictation app built with Go and Wails.
On Wayland, install ydotool for automatic text injection.
%install
rm -rf %{buildroot}
@@ -227,4 +237,4 @@ find "$RPM_TOP/RPMS" -type f -name "*.rpm" -exec cp {} "$DIST_DIR/" \;
echo ""
echo "Packages written to:"
find "$DIST_DIR" -maxdepth 1 -type f \( -name "*.deb" -o -name "*.rpm" \) -print | sort
find "$DIST_DIR" -maxdepth 1 -type f \( -name "*.deb" -o -name "*.rpm" \) -print | sort