From e4a682be2f776d751535944a5b5996d825c0ffdf Mon Sep 17 00:00:00 2001 From: pyr0ball Date: Tue, 19 May 2026 09:32:18 -0700 Subject: [PATCH] feat(patterns): mobile-origin users + dual-boot supplement system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New SourceOs variants: Android, IpadOs — routed to android-to-* and ipad-to-* pattern files respectively. Pattern bodies assume zero terminal experience; every command explained from first principles with App Store / iOS analogies. Dual-boot supplement system: PatternFile::extend() + load_supplement() in patterns.rs; lib.rs loads dualboot-{windows,macos}.toml on top of the primary pattern file when migration.dual_boot_with is set. Supplement covers NTFS dirty flag from Fast Startup, clock skew (RTC local vs UTC), GRUB overwrite by Windows Update, BitLocker, APFS/HFS+ access, T2 Secure Boot. complete_onboarding() now accepts dual_boot_with: Option and normalises it to "windows"/"macos". Onboarding.vue becomes a 3-step flow: source OS -> (Linux distro if linux) -> (dual-boot if windows/macos). Mobile users skip the dual-boot step entirely. 10 new pattern files (8 mobile + 2 supplements), config.rs tests updated. --- src-tauri/patterns/android-to-arch.toml | 136 +++++++++++++++++++ src-tauri/patterns/android-to-debian.toml | 137 +++++++++++++++++++ src-tauri/patterns/android-to-fedora.toml | 102 ++++++++++++++ src-tauri/patterns/android-to-opensuse.toml | 103 ++++++++++++++ src-tauri/patterns/dualboot-macos.toml | 65 +++++++++ src-tauri/patterns/dualboot-windows.toml | 75 +++++++++++ src-tauri/patterns/ipad-to-arch.toml | 120 +++++++++++++++++ src-tauri/patterns/ipad-to-debian.toml | 137 +++++++++++++++++++ src-tauri/patterns/ipad-to-fedora.toml | 102 ++++++++++++++ src-tauri/patterns/ipad-to-opensuse.toml | 111 +++++++++++++++ src-tauri/src/commands.rs | 17 ++- src-tauri/src/config.rs | 9 ++ src-tauri/src/lib.rs | 11 +- src-tauri/src/patterns.rs | 36 +++++ src/components/Onboarding.vue | 141 ++++++++++++++++---- 15 files changed, 1272 insertions(+), 30 deletions(-) create mode 100644 src-tauri/patterns/android-to-arch.toml create mode 100644 src-tauri/patterns/android-to-debian.toml create mode 100644 src-tauri/patterns/android-to-fedora.toml create mode 100644 src-tauri/patterns/android-to-opensuse.toml create mode 100644 src-tauri/patterns/dualboot-macos.toml create mode 100644 src-tauri/patterns/dualboot-windows.toml create mode 100644 src-tauri/patterns/ipad-to-arch.toml create mode 100644 src-tauri/patterns/ipad-to-debian.toml create mode 100644 src-tauri/patterns/ipad-to-fedora.toml create mode 100644 src-tauri/patterns/ipad-to-opensuse.toml diff --git a/src-tauri/patterns/android-to-arch.toml b/src-tauri/patterns/android-to-arch.toml new file mode 100644 index 0000000..7c5e83d --- /dev/null +++ b/src-tauri/patterns/android-to-arch.toml @@ -0,0 +1,136 @@ +[meta] +source_os = "android" +target_distro_family = "arch" + +# Android user on their first Arch/CachyOS install. +# Assumes NO terminal experience (unless they used Termux). +# Every explanation starts from first principles. +# App Store analogy: pacman/AUR = Google Play + sideloading. + +[log_paths] +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" + +# ── Package management ──────────────────────────────────────────────────────── + +[[patterns]] +id = "pacman-db-lock" +sources = ["journald", "applog:pacman"] +match_text = "could not lock database: File exists" +severity = "warn" +title = "App installer is busy" +body = "The package manager (Linux's equivalent of the Play Store) got interrupted and left a lock file. A lock file is a signal to other processes that says 'I'm running, don't start.' If nothing is installing right now, remove it: open a terminal, type exactly: sudo rm /var/lib/pacman/db.lck — then press Enter and try again. 'sudo' means 'run this as administrator.'" + +[[patterns]] +id = "partial-upgrade-warning" +sources = ["applog:pacman"] +match_text = "warning: database file for" +severity = "info" +title = "App list is out of date — update everything" +body = "On Android, updates happen automatically. On Arch Linux, you need to run updates manually — and there's an important rule: always update ALL apps at the same time, never just the list. In a terminal, type: sudo pacman -Syu — then press Enter. Enter your password when asked. The -Syu means 'sync the list AND upgrade everything.'" + +[[patterns]] +id = "pacman-dep-conflict" +sources = ["journald", "applog:pacman"] +match_text = "conflicting dependencies" +severity = "warn" +title = "Two apps conflict with each other" +body = "Two packages need something that can't be shared — like two apps that both want to be the default music player. Read the message carefully. Usually one package replaces another. Remove the old one first: sudo pacman -R — then try your install again." + +[[patterns]] +id = "aur-build-failure" +sources = ["journald", "applog:pacman"] +match_text = "error: failed to build" +severity = "warn" +title = "App build failed (AUR)" +body = "The AUR is like sideloading apps on Android — you're installing from source code that gets compiled on your machine, not a pre-built app. The build failed, usually because of a missing tool or broken code. Look at the error text above this message for the specific reason. The AUR package's comments page on aur.archlinux.org often has fixes." + +# ── Terminal basics ─────────────────────────────────────────────────────────── + +[[patterns]] +id = "command-not-found" +sources = ["journald"] +match_text = "command not found" +severity = "info" +title = "Command not found — app may not be installed" +body = "You tried to run a program that isn't installed. On Android, apps are visible in the drawer; on Linux, they're invisible until you ask for them. To find and install the missing program, try: sudo pacman -Ss — this searches for it. Then install with: sudo pacman -S ." + +[[patterns]] +id = "permission-denied" +sources = ["journald"] +match_text = "Permission denied" +severity = "info" +title = "Permission denied" +body = "Linux has a permission system where files and folders are owned by specific users. This is more visible here than on Android. If you need admin access for a command, put 'sudo' before it — like: sudo . For files you own but can't access, check ownership with: ls -la /path/to/file" + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Hardware driver file missing" +body = "On Android, drivers come pre-installed and invisible. On Linux, some hardware needs a separate firmware file — like a plugin for your Wi-Fi chip or graphics card. Install the main firmware package: sudo pacman -S linux-firmware — then restart. If a specific device still doesn't work, the error message above will name which firmware file is missing." + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "System ran out of memory — closed an app" +body = "Linux ran out of RAM and had to close a program, similar to Android killing background apps. If this keeps happening, close programs you're not using, or add 'swap' (disk space used as overflow RAM): sudo pacman -S zram-generator" + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Storage error" +body = "Something went wrong reading or writing to the drive. This could be a hardware problem. Check drive health: sudo smartctl -a /dev/sda — first install the tool: sudo pacman -S smartmontools" + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "Sound system not responding" +body = "PipeWire is the audio manager — like the sound settings system inside Android. Restart it: open a terminal and type: systemctl --user restart pipewire pipewire-pulse wireplumber — if sound still doesn't work, log out and log back in." + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth blocked by software switch" +body = "A software setting is blocking Bluetooth — like Airplane Mode on your phone. Run: rfkill unblock bluetooth — in a terminal. If it shows 'Hard blocked', there's a physical switch or BIOS setting to check." + +[[patterns]] +id = "bluetooth-profile-unavailable" +sources = ["journald"] +match_text = "br-connection-profile-unavailable" +severity = "info" +title = "Bluetooth audio profile missing" +body = "Your Bluetooth device connected but the right audio mode isn't available. Install: sudo pacman -S pipewire-bluetooth — then restart Bluetooth: sudo systemctl restart bluetooth" + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "Wi-Fi connection failed" +body = "Linux couldn't connect to the network. Common causes: wrong password, or the Wi-Fi driver isn't loaded. Check connection status: nmcli device status — if the Wi-Fi adapter doesn't appear, the driver may need to be installed." + +# ── GPU ─────────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "gpu-hang" +sources = ["kmsg"] +match_text = "GPU HANG" +severity = "warn" +title = "Graphics card stopped responding" +body = "The graphics card froze and the driver recovered it — like a forced restart of the GPU. Games or video apps may have crashed. If this keeps happening, check that your graphics drivers are current: sudo pacman -Syu" diff --git a/src-tauri/patterns/android-to-debian.toml b/src-tauri/patterns/android-to-debian.toml new file mode 100644 index 0000000..906f5ca --- /dev/null +++ b/src-tauri/patterns/android-to-debian.toml @@ -0,0 +1,137 @@ +[meta] +source_os = "android" +target_distro_family = "debian" + +# Android user on their first Debian/Ubuntu/Mint install. +# Assumes NO terminal experience. Ubuntu/Mint are the recommended starting points +# for Android migrants because of automatic updates and GUI app stores (GNOME Software/Discover). + +[log_paths] +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" + +# ── Package management ──────────────────────────────────────────────────────── + +[[patterns]] +id = "apt-lock" +sources = ["journald"] +match_text = "Could not get lock /var/lib/dpkg/lock" +severity = "warn" +title = "App installer is busy" +body = "The software installer (Ubuntu/Mint calls it 'apt') is already running — probably doing automatic background updates, similar to how Android apps update silently. Wait a minute and try again. If it's stuck: open a terminal and type: sudo rm /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock — then: sudo dpkg --configure -a" + +[[patterns]] +id = "dpkg-interrupted" +sources = ["journald"] +match_text = "dpkg was interrupted" +severity = "warn" +title = "App install was cut short" +body = "A previous install didn't finish cleanly — like pulling the charging cable out mid-update on your phone. Fix it: open a terminal and type: sudo dpkg --configure -a — then try your install again." + +[[patterns]] +id = "apt-unmet-dependency" +sources = ["journald"] +match_text = "Unmet dependencies" +severity = "warn" +title = "App needs another app first" +body = "The software you're trying to install needs something else installed first — similar to a game on Android requiring Google Play Services. Let the installer fix it automatically: sudo apt --fix-broken install" + +# ── Terminal basics ─────────────────────────────────────────────────────────── + +[[patterns]] +id = "permission-denied" +sources = ["journald"] +match_text = "Permission denied" +severity = "info" +title = "Permission denied" +body = "Linux files and folders are protected by a permission system. If a command fails with this error, you may need to run it as admin — put 'sudo' before the command: sudo — and enter your password. Your password won't show as you type, that's normal." + +# ── AppArmor ────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "apparmor-denial" +sources = ["journald"] +match_text = "apparmor=\"DENIED\"" +severity = "info" +title = "App blocked by security policy" +body = "Ubuntu/Debian includes a security layer called AppArmor — like Android's app permissions system, but for the whole operating system. An app tried to do something outside its allowed permissions. Usually this resolves itself; if an app keeps failing, check: sudo aa-status" + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Hardware driver file missing" +body = "Some hardware needs a 'firmware' file — a small program that tells Linux how to talk to a specific chip. Install the main firmware package: sudo apt install firmware-linux linux-firmware — restart after. Ubuntu usually handles this automatically; you may see this on Debian." + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "System ran out of memory — closed an app" +body = "Linux had to close a program to free up memory — similar to Android killing background apps when RAM is full. If this keeps happening, try closing programs you're not using." + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Storage error" +body = "Something went wrong reading or writing to the drive. This is a hardware-level issue. Install a diagnostic tool: sudo apt install smartmontools — then check: sudo smartctl -a /dev/sda" + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "Sound system not responding" +body = "PipeWire is the audio manager — restart it: systemctl --user restart pipewire pipewire-pulse wireplumber — if that doesn't help, log out and back in." + +[[patterns]] +id = "pulseaudio-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to pulseaudio" +severity = "warn" +title = "Sound system not responding" +body = "The audio system (PulseAudio) stopped working. Restart it: pulseaudio --kill && pulseaudio --start — or log out and back in." + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth blocked by software switch" +body = "Run: rfkill unblock bluetooth — in a terminal. Like turning Airplane Mode off on your phone." + +[[patterns]] +id = "cups-server-error" +sources = ["journald"] +match_text = "Unable to connect to CUPS server" +severity = "info" +title = "Printer service not running" +body = "The printing service isn't running. Unlike Android where you'd use a manufacturer app, Linux uses a universal print system called CUPS. Start it: sudo systemctl start cups && sudo systemctl enable cups" + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "Wi-Fi connection failed" +body = "Couldn't connect to the network. Double-check the password, or try: nmcli device status — in a terminal to see your network devices." + +# ── Media ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "missing-codec" +sources = ["journald"] +match_text = "GStreamer: Failed to find plugin" +severity = "info" +title = "Media format not supported" +body = "Linux doesn't include some video/audio formats by default for legal reasons — unlike Android which bundles them. Install them on Ubuntu/Mint: sudo apt install ubuntu-restricted-extras — this adds MP3, MP4, and other common formats." diff --git a/src-tauri/patterns/android-to-fedora.toml b/src-tauri/patterns/android-to-fedora.toml new file mode 100644 index 0000000..381c466 --- /dev/null +++ b/src-tauri/patterns/android-to-fedora.toml @@ -0,0 +1,102 @@ +[meta] +source_os = "android" +target_distro_family = "fedora" + +# Android user on their first Fedora install. +# Assumes NO terminal experience. All explanations from first principles. + +[log_paths] +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" + +# ── Package management ──────────────────────────────────────────────────────── + +[[patterns]] +id = "dnf-lock" +sources = ["journald"] +match_text = "Another app is currently holding the dnf lock" +severity = "warn" +title = "App installer is busy" +body = "Fedora's software installer (dnf) is already running — probably automatic background updates, similar to how Android apps update silently. Wait a minute. If it's stuck: open a terminal and type: sudo killall dnf — then try again." + +[[patterns]] +id = "dnf-dep-conflict" +sources = ["journald"] +match_text = "conflicts with" +severity = "warn" +title = "Two apps conflict with each other" +body = "Two packages need something that can't be shared. Let Fedora try to fix it: sudo dnf distro-sync — this brings everything back into a consistent state." + +# ── SELinux ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "selinux-denial" +sources = ["journald"] +match_text = "type=AVC" +severity = "info" +title = "Security system blocked an action" +body = "Fedora includes SELinux — a security layer that controls what each program is allowed to do, more detailed than Android's app permissions. This is usually a normal event, not a problem. If an app keeps failing, check what's being blocked: ausearch -m AVC -ts recent" + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Hardware driver file missing" +body = "Some hardware needs a firmware file — a program that tells Linux how to talk to a specific chip. Install it: sudo dnf install linux-firmware — restart after. For some hardware (especially older WiFi cards), you may also need: sudo dnf install https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm" + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "System ran out of memory — closed an app" +body = "Linux had to close a program to free up RAM, like Android killing background apps. If this keeps happening, try closing some programs." + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Storage error" +body = "A hardware-level storage error. Install a diagnostic tool: sudo dnf install smartmontools — then check: sudo smartctl -a /dev/sda" + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "Sound system not responding" +body = "Restart the audio system: systemctl --user restart pipewire pipewire-pulse wireplumber — or log out and back in." + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth blocked by software switch" +body = "Run: rfkill unblock bluetooth — in a terminal." + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "Wi-Fi connection failed" +body = "Couldn't connect to the network. Check: nmcli device status — in a terminal. If the adapter doesn't appear, check: dmesg | grep firmware — a missing driver may be the cause." + +# ── Media ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "missing-codec" +sources = ["journald"] +match_text = "GStreamer: Failed to find plugin" +severity = "info" +title = "Media format not supported" +body = "Linux doesn't include some video/audio formats by default. Install them from RPM Fusion: first enable it: sudo dnf install https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm — then: sudo dnf install gstreamer1-plugins-bad-free gstreamer1-plugins-ugly" diff --git a/src-tauri/patterns/android-to-opensuse.toml b/src-tauri/patterns/android-to-opensuse.toml new file mode 100644 index 0000000..80bdf89 --- /dev/null +++ b/src-tauri/patterns/android-to-opensuse.toml @@ -0,0 +1,103 @@ +[meta] +source_os = "android" +target_distro_family = "opensuse" + +# Android user on their first openSUSE install. +# Assumes NO terminal experience. openSUSE's YaST GUI tool is a good entry point +# for users unfamiliar with terminal-based administration. + +[log_paths] +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" + +# ── Package management ──────────────────────────────────────────────────────── + +[[patterns]] +id = "zypper-lock" +sources = ["journald"] +match_text = "System management is locked" +severity = "warn" +title = "App installer is busy" +body = "The software installer (zypper) is already running. Wait a minute. If it's stuck: open a terminal and type: sudo zypper ps — to see what's using it." + +[[patterns]] +id = "zypper-dep-conflict" +sources = ["journald"] +match_text = "conflicts with" +severity = "warn" +title = "Two apps conflict with each other" +body = "Two packages need something that can't be shared. Run a full update to resolve: sudo zypper dup — then try again." + +# ── AppArmor ────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "apparmor-denial" +sources = ["journald"] +match_text = "apparmor=\"DENIED\"" +severity = "info" +title = "App blocked by security policy" +body = "openSUSE includes AppArmor — a security layer like Android's app permissions but for the whole system. An app was blocked from doing something. Check: sudo aa-status — YaST also has an AppArmor section under Security." + +# ── YaST ────────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "yast-backend-fail" +sources = ["journald"] +match_text = "YaST got signal" +severity = "warn" +title = "Settings tool crashed" +body = "YaST is openSUSE's settings tool — like the Settings app on Android but for the whole system. It crashed. Try: sudo yast2 — in a terminal to run the text version, which is more stable." + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Hardware driver file missing" +body = "Install the firmware package: sudo zypper install kernel-firmware — restart after." + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "System ran out of memory — closed an app" +body = "Linux had to close a program to free up RAM, like Android killing background apps. If this keeps happening, openSUSE's YaST -> System -> Partitioner can add or resize swap space." + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Storage error" +body = "A hardware-level storage error. Install: sudo zypper install smartmontools — then check: sudo smartctl -a /dev/sda" + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "Sound system not responding" +body = "Restart the audio system: systemctl --user restart pipewire pipewire-pulse wireplumber — or log out and back in." + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth blocked by software switch" +body = "Run: rfkill unblock bluetooth — in a terminal. YaST -> Network -> Bluetooth also has a toggle." + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "Wi-Fi connection failed" +body = "Couldn't connect to the network. Check: nmcli device status — or use YaST -> Network Settings to diagnose." diff --git a/src-tauri/patterns/dualboot-macos.toml b/src-tauri/patterns/dualboot-macos.toml new file mode 100644 index 0000000..eb300da --- /dev/null +++ b/src-tauri/patterns/dualboot-macos.toml @@ -0,0 +1,65 @@ +[meta] +source_os = "supplement" +target_distro_family = "any" + +# Supplementary patterns for users dual-booting macOS alongside any Linux distro. +# These patterns cover coexistence-specific issues unique to Apple hardware. +# This file is merged on top of the primary migration pattern file. + +[log_paths] + +# ── Apple T2 / Secure Boot ──────────────────────────────────────────────────── + +[[patterns]] +id = "t2-secure-boot" +sources = ["kmsg", "journald"] +match_text = "Secure Boot" +severity = "warn" +title = "Apple T2 Secure Boot blocking Linux" +body = "Intel Macs with a T2 chip require Secure Boot to be disabled before Linux can boot. Boot into macOS Recovery (hold Cmd+R at startup) -> Utilities -> Startup Security Utility -> set Secure Boot to 'No Security' and allow booting from external media. Apple Silicon (M1/M2) Macs cannot dual-boot Linux at all — see Asahi Linux for the current state." + +[[patterns]] +id = "apple-wifi-firmware" +sources = ["kmsg"] +match_text = "brcmfmac: brcmf_fw_alloc_request" +severity = "warn" +title = "Apple WiFi firmware not loading" +body = "Broadcom WiFi chips in Macs need proprietary firmware. Extract it from the macOS partition: mount your macOS partition and copy from /Volumes/Macintosh HD/usr/share/firmware/wifi/ — or install apple-firmware-wifi (check your distro's AUR or repos)." + +# ── HFS+ / APFS ─────────────────────────────────────────────────────────────── + +[[patterns]] +id = "apfs-not-mounted" +sources = ["journald"] +match_text = "apfs: module not found" +severity = "info" +title = "macOS APFS partition not readable" +body = "Linux can't read APFS (macOS's filesystem) natively. To access files: sudo apt install apfs-fuse (Debian) or paru -S apfs-fuse-git (Arch). Mount: apfs-fuse /dev/sdXN /mnt/mac — read-only access only." + +[[patterns]] +id = "hfsplus-not-mounted" +sources = ["journald"] +match_text = "hfsplus: Journal not clean" +severity = "warn" +title = "HFS+ partition not cleanly unmounted" +body = "The macOS HFS+ partition (older Macs) wasn't unmounted cleanly. Mount in macOS and run Disk Utility -> First Aid to fix it. Or force Linux mount: sudo mount -o force /dev/sdXN /mnt/mac" + +# ── rEFInd / boot manager ──────────────────────────────────────────────────── + +[[patterns]] +id = "refind-missing" +sources = ["journald"] +match_text = "Boot0001" +severity = "info" +title = "EFI boot entry may be missing" +body = "macOS may have reset the EFI boot order after an update, removing the Linux entry. rEFInd is the recommended boot manager for Mac dual-boot: it auto-detects both macOS and Linux. Install: sudo refind-install — or reinstall GRUB EFI and re-add it with efibootmgr." + +# ── Clock skew ──────────────────────────────────────────────────────────────── + +[[patterns]] +id = "rtc-time-wrong" +sources = ["journald"] +match_text = "RTC time" +severity = "info" +title = "System clock drifted after macOS boot" +body = "macOS stores the hardware clock in local time; Linux stores it in UTC. This causes clock drift in dual-boot. Fix in Linux: timedatectl set-local-rtc 0 — then set macOS to UTC by running in Terminal: sudo systemsetup -setusingnetworktime off && sudo systemsetup -settime $(date -u +%H:%M:%S)" diff --git a/src-tauri/patterns/dualboot-windows.toml b/src-tauri/patterns/dualboot-windows.toml new file mode 100644 index 0000000..4cf81e8 --- /dev/null +++ b/src-tauri/patterns/dualboot-windows.toml @@ -0,0 +1,75 @@ +[meta] +source_os = "supplement" +target_distro_family = "any" + +# Supplementary patterns for users dual-booting Windows alongside any Linux distro. +# These patterns cover coexistence-specific issues that only appear because both OSes +# share the same hardware. This file is merged on top of the primary migration pattern file. + +[log_paths] + +# ── NTFS / Fast Startup ─────────────────────────────────────────────────────── + +[[patterns]] +id = "ntfs-volume-dirty" +sources = ["kmsg"] +match_text = "volume is dirty" +severity = "warn" +title = "Windows drive needs a check (Fast Startup)" +body = "Windows didn't shut down cleanly — it probably used Fast Startup (hibernation). Linux mounted the NTFS partition read-only to protect your data. Fix in Windows: Start -> Power -> hold Shift and click Shut Down (real shutdown, not hibernate). Then turn Fast Startup off: Control Panel -> Power Options -> 'Choose what the power buttons do' -> uncheck 'Turn on fast startup'." + +[[patterns]] +id = "ntfs-force-required" +sources = ["kmsg"] +match_text = "Dirty flag is set" +severity = "warn" +title = "NTFS drive mounted read-only (dirty flag)" +body = "Windows left the NTFS filesystem marked dirty. Boot into Windows and do a full shutdown (Shift+Shut Down), or force-fix on Linux: sudo ntfsfix /dev/sdXN — replace sdXN with the partition shown in the error." + +[[patterns]] +id = "ntfs-hibernation" +sources = ["kmsg"] +match_text = "Windows is hibernated" +severity = "warn" +title = "Windows is hibernated — partition locked" +body = "Linux found a Windows hibernation file (hiberfil.sys) and can't write to the NTFS partition safely. You must resume and properly shut down Windows first. To remove the hibernation file permanently: in Windows as admin, run: powercfg /h off" + +# ── Clock skew ──────────────────────────────────────────────────────────────── + +[[patterns]] +id = "rtc-time-wrong" +sources = ["journald"] +match_text = "RTC time" +severity = "info" +title = "System clock drifted after Windows boot" +body = "Windows stores the hardware clock in local time; Linux stores it in UTC. Dual-booting causes clock drift between sessions. Fix permanently in Linux: timedatectl set-local-rtc 0 (keep Linux correct and fix Windows instead). Or in Windows, add a registry key to use UTC: HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation, add DWORD RealTimeIsUniversal = 1." + +# ── GRUB overwritten by Windows ─────────────────────────────────────────────── + +[[patterns]] +id = "grub-missing-windows-update" +sources = ["journald"] +match_text = "error: no such device" +severity = "warn" +title = "GRUB may have been overwritten" +body = "Windows Update sometimes overwrites the EFI boot entry. If Linux stopped booting after a Windows update: boot from your Linux USB installer -> rescue/chroot -> reinstall GRUB: grub-install /dev/sdX && update-grub (Debian/Ubuntu) or grub-install /dev/sdX && grub-mkconfig -o /boot/grub/grub.cfg (Arch)." + +# ── BitLocker ───────────────────────────────────────────────────────────────── + +[[patterns]] +id = "bitlocker-blocked" +sources = ["kmsg"] +match_text = "BitLocker" +severity = "info" +title = "BitLocker encrypted partition" +body = "This Windows partition is BitLocker-encrypted. Linux can mount it with dislocker: sudo apt install dislocker (Debian) or paru -S dislocker (Arch). You'll need the BitLocker recovery key from your Microsoft account." + +# ── Shared NTFS partition permissions ──────────────────────────────────────── + +[[patterns]] +id = "ntfs-permission-error" +sources = ["journald"] +match_text = "ntfs-3g: Failed to open" +severity = "warn" +title = "NTFS permission error" +body = "ntfs-3g can't open the Windows partition. Check your /etc/fstab mount options — add uid=1000,gid=1000,umask=022 to give your Linux user access. Make sure Windows is fully shut down first." diff --git a/src-tauri/patterns/ipad-to-arch.toml b/src-tauri/patterns/ipad-to-arch.toml new file mode 100644 index 0000000..e8d9eb3 --- /dev/null +++ b/src-tauri/patterns/ipad-to-arch.toml @@ -0,0 +1,120 @@ +[meta] +source_os = "ipad" +target_distro_family = "arch" + +# iPad/iPhone user on their first Arch/CachyOS install. +# More sandboxed mental model than Android — no file manager, no sideloading concept, +# everything lived inside apps. Arch is a steep starting point; body text is encouraging +# but honest about the learning curve. + +[log_paths] +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" + +# ── Package management ──────────────────────────────────────────────────────── + +[[patterns]] +id = "pacman-db-lock" +sources = ["journald", "applog:pacman"] +match_text = "could not lock database: File exists" +severity = "warn" +title = "App installer is busy" +body = "The package manager (pacman — Linux's version of the App Store, but text-based) got interrupted and left a lock file behind. Think of it like an App Store update that got cut off. If nothing is currently installing, remove the lock: open a terminal (called 'Konsole' or 'Alacritty' on your system) and type: sudo rm /var/lib/pacman/db.lck — then press Enter. 'sudo' means run as administrator; your password won't show as you type." + +[[patterns]] +id = "partial-upgrade-warning" +sources = ["applog:pacman"] +match_text = "warning: database file for" +severity = "info" +title = "App list out of date — update everything together" +body = "On iPad, updates happen automatically and silently. On Arch Linux, you run them manually. Important rule: always update everything at once. In a terminal, type: sudo pacman -Syu — press Enter, enter your password. This refreshes the app list AND updates all installed software." + +[[patterns]] +id = "aur-build-failure" +sources = ["journald", "applog:pacman"] +match_text = "error: failed to build" +severity = "warn" +title = "App build failed')" +body = "The AUR (Arch User Repository) is software that gets compiled on your machine from source code — there's no real iOS equivalent since Apple controls all distribution. A build failed. Look at the error text above this notification for the specific cause. The AUR package's comment page on aur.archlinux.org often has workarounds." + +# ── Files and paths ─────────────────────────────────────────────────────────── + +[[patterns]] +id = "permission-denied" +sources = ["journald"] +match_text = "Permission denied" +severity = "info" +title = "Permission denied" +body = "On iPad, every app lives in its own private sandbox — you never think about file permissions. On Linux, files are shared between users and programs, and access is controlled by permissions. If you need admin rights for a command, add 'sudo' before it: sudo — and enter your password." + +[[patterns]] +id = "no-such-file" +sources = ["journald"] +match_text = "No such file or directory" +severity = "info" +title = "File or folder not found" +body = "On iPad, files lived inside apps and you never typed paths. On Linux, files have locations like /home/username/Documents. Check that the path you typed is correct — Linux paths are case-sensitive ('Documents' and 'documents' are different). Use 'ls' to list files in the current folder." + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Hardware driver file missing" +body = "On iPad, Apple controls all hardware and drivers — you never see this. On Linux, some hardware components need a driver file installed separately. Install the main driver package: sudo pacman -S linux-firmware — then restart your computer." + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "System ran out of memory — closed an app" +body = "Linux had to close a program to free up RAM — similar to iPadOS refreshing apps in the background when RAM runs low. If this keeps happening, consider closing unused programs or adding 'zram' (uses storage as extra RAM): sudo pacman -S zram-generator" + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Storage error" +body = "Something went wrong with the drive. Install a check tool: sudo pacman -S smartmontools — then run: sudo smartctl -a /dev/sda" + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "Sound system not responding" +body = "PipeWire manages audio on this system — like the iOS audio system, but you can restart it. Type in a terminal: systemctl --user restart pipewire pipewire-pulse wireplumber — if sound still doesn't work, log out and log back in." + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth turned off by software" +body = "A software setting is blocking Bluetooth — like enabling Airplane Mode. Turn it back on: rfkill unblock bluetooth — in a terminal. If it says 'Hard blocked', there's a physical switch on your computer or a setting in the BIOS." + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "Wi-Fi connection failed" +body = "Couldn't connect to the network. Check status: nmcli device status — in a terminal. If the Wi-Fi adapter doesn't appear in the list, the driver may not be loaded." + +# ── GPU ─────────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "gpu-hang" +sources = ["kmsg"] +match_text = "GPU HANG" +severity = "warn" +title = "Graphics card stopped responding" +body = "The graphics system crashed and recovered — similar to an app freezing on iPad, but at a lower level. If this keeps happening during games or video, update your graphics drivers: sudo pacman -Syu" diff --git a/src-tauri/patterns/ipad-to-debian.toml b/src-tauri/patterns/ipad-to-debian.toml new file mode 100644 index 0000000..4f0cdb0 --- /dev/null +++ b/src-tauri/patterns/ipad-to-debian.toml @@ -0,0 +1,137 @@ +[meta] +source_os = "ipad" +target_distro_family = "debian" + +# iPad/iPhone user on their first Debian/Ubuntu/Mint install. +# Ubuntu/Mint are the most recommended starting points for iPad migrants — +# automatic updates, GUI software center, familiar GNOME/Cinnamon interface. + +[log_paths] +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" + +# ── Package management ──────────────────────────────────────────────────────── + +[[patterns]] +id = "apt-lock" +sources = ["journald"] +match_text = "Could not get lock /var/lib/dpkg/lock" +severity = "warn" +title = "Software installer is busy" +body = "Ubuntu's software installer is already running in the background — probably doing automatic updates, similar to how iOS updates apps silently. Wait a minute and try again. You can also open 'Software Updater' from the app menu to see what's happening. If it's been stuck for a long time: open the Terminal app and type: sudo rm /var/lib/dpkg/lock-frontend && sudo dpkg --configure -a" + +[[patterns]] +id = "dpkg-interrupted" +sources = ["journald"] +match_text = "dpkg was interrupted" +severity = "warn" +title = "App install was cut short" +body = "A previous software install didn't finish. Fix it: open Terminal and type: sudo dpkg --configure -a — then press Enter." + +[[patterns]] +id = "apt-unmet-dependency" +sources = ["journald"] +match_text = "Unmet dependencies" +severity = "warn" +title = "App needs another app first" +body = "The software you're installing needs something else first. Let Ubuntu fix it: sudo apt --fix-broken install" + +# ── AppArmor ────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "apparmor-denial" +sources = ["journald"] +match_text = "apparmor=\"DENIED\"" +severity = "info" +title = "App blocked by security policy" +body = "Ubuntu includes AppArmor — a security layer that restricts what each program can do, similar to how iOS tightly sandboxes every app. Something was blocked. This is usually routine security protection, not a problem." + +# ── Files and paths ─────────────────────────────────────────────────────────── + +[[patterns]] +id = "permission-denied" +sources = ["journald"] +match_text = "Permission denied" +severity = "info" +title = "Permission denied" +body = "On iPad, files are hidden inside apps and permissions are invisible. On Linux, files are shared and controlled. If a command fails with this, add 'sudo' before it to run as admin: sudo — your password won't show as you type, that's normal." + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Hardware driver file missing" +body = "Some hardware needs a driver file installed separately — unlike iPad where Apple controls everything. Ubuntu usually handles this, but if something isn't working: sudo apt install firmware-linux linux-firmware — then restart." + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "System ran out of memory" +body = "Linux closed a program to free up RAM — similar to iPadOS refreshing apps in the background. Try closing some programs you're not using." + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Storage error" +body = "A hardware-level storage error. Install: sudo apt install smartmontools — then check: sudo smartctl -a /dev/sda" + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "Sound system not responding" +body = "Restart audio: systemctl --user restart pipewire pipewire-pulse wireplumber — or log out and back in." + +[[patterns]] +id = "pulseaudio-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to pulseaudio" +severity = "warn" +title = "Sound system not responding" +body = "Restart audio: pulseaudio --kill && pulseaudio --start — or log out and back in." + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth turned off by software" +body = "Run: rfkill unblock bluetooth — in a terminal." + +[[patterns]] +id = "cups-server-error" +sources = ["journald"] +match_text = "Unable to connect to CUPS server" +severity = "info" +title = "Printer service not running" +body = "The printing service needs to be started: sudo systemctl start cups && sudo systemctl enable cups — then try printing again from your app." + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "Wi-Fi connection failed" +body = "Check the network status icon in the top bar — or open Terminal and type: nmcli device status" + +# ── Media ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "missing-codec" +sources = ["journald"] +match_text = "GStreamer: Failed to find plugin" +severity = "info" +title = "Video or audio format not supported" +body = "Linux needs extra packages to play some media formats. On Ubuntu/Mint: sudo apt install ubuntu-restricted-extras — this adds support for MP3, MP4, and other common formats." diff --git a/src-tauri/patterns/ipad-to-fedora.toml b/src-tauri/patterns/ipad-to-fedora.toml new file mode 100644 index 0000000..fe77b98 --- /dev/null +++ b/src-tauri/patterns/ipad-to-fedora.toml @@ -0,0 +1,102 @@ +[meta] +source_os = "ipad" +target_distro_family = "fedora" + +# iPad/iPhone user on their first Fedora install. +# Assumes NO terminal experience. Fedora GNOME is visually close to iPadOS. + +[log_paths] +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" + +# ── Package management ──────────────────────────────────────────────────────── + +[[patterns]] +id = "dnf-lock" +sources = ["journald"] +match_text = "Another app is currently holding the dnf lock" +severity = "warn" +title = "Software installer is busy" +body = "Fedora is doing automatic updates in the background — like iOS updating apps silently. Wait a minute and try again. You can see what's happening in GNOME Software (the App Store equivalent)." + +[[patterns]] +id = "dnf-dep-conflict" +sources = ["journald"] +match_text = "conflicts with" +severity = "warn" +title = "Two apps conflict with each other" +body = "Two packages need something that can't coexist. Run: sudo dnf distro-sync — in a terminal to bring everything back into sync." + +# ── SELinux ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "selinux-denial" +sources = ["journald"] +match_text = "type=AVC" +severity = "info" +title = "Security system blocked an action" +body = "Fedora uses SELinux — a detailed security system that controls what each program can access. Think of it as a much stricter version of iOS app permissions. This log entry is usually routine. If an app keeps failing, GNOME's 'Setroubleshoot' tool will pop up with an explanation and fix." + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Hardware driver file missing" +body = "Some hardware needs a driver installed separately. Fedora usually handles this automatically, but if a device isn't working: sudo dnf install linux-firmware — restart after." + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "System ran out of memory" +body = "Linux closed a program to free RAM — like iPadOS refreshing background apps. Close unused programs." + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Storage error" +body = "Install: sudo dnf install smartmontools — then check: sudo smartctl -a /dev/sda" + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "Sound system not responding" +body = "Restart audio: systemctl --user restart pipewire pipewire-pulse wireplumber — or log out and back in." + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth turned off by software" +body = "Run: rfkill unblock bluetooth — in a terminal." + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "Wi-Fi connection failed" +body = "Check the network icon in the top bar, or: nmcli device status — in a terminal." + +# ── Media ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "missing-codec" +sources = ["journald"] +match_text = "GStreamer: Failed to find plugin" +severity = "info" +title = "Video or audio format not supported" +body = "Fedora needs extra packages for some media formats. Enable RPM Fusion: sudo dnf install https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm — then: sudo dnf install gstreamer1-plugins-bad-free gstreamer1-plugins-ugly" diff --git a/src-tauri/patterns/ipad-to-opensuse.toml b/src-tauri/patterns/ipad-to-opensuse.toml new file mode 100644 index 0000000..c18f1f2 --- /dev/null +++ b/src-tauri/patterns/ipad-to-opensuse.toml @@ -0,0 +1,111 @@ +[meta] +source_os = "ipad" +target_distro_family = "opensuse" + +# iPad/iPhone user on their first openSUSE install. +# YaST (openSUSE's graphical admin tool) is a good bridge for users +# unfamiliar with terminal-based system administration. + +[log_paths] +steam = "~/.local/share/Steam/logs/content_log.txt" +proton = "~/.local/share/Steam/logs/proton_log.txt" + +# ── Package management ──────────────────────────────────────────────────────── + +[[patterns]] +id = "zypper-lock" +sources = ["journald"] +match_text = "System management is locked" +severity = "warn" +title = "Software installer is busy" +body = "openSUSE's software manager is running — like iOS doing background updates. Wait a minute, or open YaST -> Software -> Software Management to see what's happening." + +[[patterns]] +id = "zypper-dep-conflict" +sources = ["journald"] +match_text = "conflicts with" +severity = "warn" +title = "Two apps conflict" +body = "Run a full update: sudo zypper dup — this resolves most conflicts." + +# ── AppArmor ────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "apparmor-denial" +sources = ["journald"] +match_text = "apparmor=\"DENIED\"" +severity = "info" +title = "App blocked by security policy" +body = "openSUSE uses AppArmor for security — similar to how iOS isolates apps. Something was blocked. YaST -> Security -> AppArmor Configuration shows active profiles." + +# ── YaST ────────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "yast-backend-fail" +sources = ["journald"] +match_text = "YaST got signal" +severity = "warn" +title = "Settings tool crashed" +body = "YaST (openSUSE's settings tool) crashed. Try running it from a terminal: sudo yast2 — the text mode is more stable than the graphical version." + +# ── System ──────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "kernel-driver-firmware" +sources = ["kmsg"] +match_text = "firmware: failed to load" +severity = "warn" +title = "Hardware driver file missing" +body = "Install the firmware package: sudo zypper install kernel-firmware — restart after." + +[[patterns]] +id = "oom-killer" +sources = ["kmsg"] +match_text = "Out of memory: Kill process" +severity = "warn" +title = "System ran out of memory" +body = "Linux closed a program to free RAM. YaST -> System -> Partitioner can add or resize swap space (overflow RAM stored on disk)." + +[[patterns]] +id = "disk-io-error" +sources = ["kmsg"] +match_text = "Buffer I/O error on device" +severity = "warn" +title = "Storage error" +body = "Install: sudo zypper install smartmontools — then: sudo smartctl -a /dev/sda" + +# ── Audio ───────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "pipewire-connect-fail" +sources = ["journald"] +match_text = "Failed to connect to PipeWire" +severity = "warn" +title = "Sound system not responding" +body = "Restart audio: systemctl --user restart pipewire pipewire-pulse wireplumber — or log out and back in." + +[[patterns]] +id = "bluetooth-rfkill-blocked" +sources = ["journald"] +match_text = "Blocked through rfkill" +severity = "warn" +title = "Bluetooth turned off by software" +body = "Run: rfkill unblock bluetooth — or use YaST -> Network -> Bluetooth." + +[[patterns]] +id = "cups-server-error" +sources = ["journald"] +match_text = "Unable to connect to CUPS server" +severity = "info" +title = "Printer service not running" +body = "Start printing: sudo systemctl start cups && sudo systemctl enable cups — or use YaST -> Hardware -> Printer." + +# ── Network ─────────────────────────────────────────────────────────────────── + +[[patterns]] +id = "networkmanager-activation-fail" +sources = ["journald"] +match_text = "Activation failed" +severity = "info" +title = "Wi-Fi connection failed" +body = "Check YaST -> Network Settings — or: nmcli device status — in a terminal." diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 77f6db4..4cc36f1 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -29,12 +29,15 @@ pub fn complete_onboarding( source_os: String, distro: String, source_distro: Option, + dual_boot_with: Option, state: State<'_, AppState>, ) -> Result<(), String> { let source = match source_os.to_lowercase().as_str() { "macos" | "mac" => SourceOs::Macos, "windows" => SourceOs::Windows, "linux" => SourceOs::Linux, + "android" => SourceOs::Android, + "ipad" | "ios" | "ipados" => SourceOs::IpadOs, _ => SourceOs::Unknown, }; @@ -44,18 +47,24 @@ pub fn complete_onboarding( distro }; - // For Linux-to-Linux migrations, derive source family from the reported source distro. - // If a source_distro was passed explicitly, use distro_family() on it; otherwise leave None. - let source_distro_family = source_distro.as_deref().map(|sd| { + let source_distro_family = source_distro.as_deref().and_then(|sd| { let family = crate::distro::distro_family(sd); if family == "unknown" { None } else { Some(family.to_string()) } - }).flatten(); + }); + + // Normalise dual_boot_with to a canonical name; reject unrecognised values. + let dual_boot_with = dual_boot_with.and_then(|s| match s.to_lowercase().as_str() { + "windows" => Some("windows".to_string()), + "macos" | "mac" => Some("macos".to_string()), + _ => None, + }); let mut config = state.config.lock().map_err(|e| e.to_string())?; config.migration = Some(MigrationConfig { source_os: source, distro: detected, source_distro_family, + dual_boot_with, fluency_level: 0, }); config.save().map_err(|e| e.to_string()) diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index 68ed20f..31e5d0f 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -8,6 +8,8 @@ pub enum SourceOs { Macos, Windows, Linux, + Android, + IpadOs, Unknown, } @@ -38,6 +40,11 @@ pub struct MigrationConfig { /// Used to load a more specific pattern file (e.g. debian-to-arch) before the generic linux-to-arch fallback. #[serde(default)] pub source_distro_family: Option, + /// Co-installed OS for dual-boot setups, e.g. "windows", "macos". + /// When set, a supplementary pattern file (dualboot-windows.toml) is loaded on top of + /// the primary migration patterns to surface coexistence-specific issues. + #[serde(default)] + pub dual_boot_with: Option, /// 0–5: grows as user dismisses suggestions they already know pub fluency_level: u8, } @@ -143,6 +150,7 @@ mod tests { source_os: SourceOs::Macos, distro: "cachyos".into(), source_distro_family: None, + dual_boot_with: None, fluency_level: 0, }); assert!(!config.needs_onboarding()); @@ -155,6 +163,7 @@ mod tests { source_os: SourceOs::Windows, distro: "linuxmint".into(), source_distro_family: None, + dual_boot_with: None, fluency_level: 2, }), ..Default::default() diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 3033771..62b5eb4 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -40,9 +40,18 @@ pub fn run() { config::SourceOs::Macos => "macos", config::SourceOs::Windows => "windows", config::SourceOs::Linux => "linux", + config::SourceOs::Android => "android", + config::SourceOs::IpadOs => "ipad", config::SourceOs::Unknown => "unknown", }; - patterns::load(source, migration.source_distro_family.as_deref(), family).ok() + let mut pf = patterns::load(source, migration.source_distro_family.as_deref(), family).ok(); + // Layer dual-boot supplement patterns on top when a co-installed OS is configured. + if let (Some(ref mut primary), Some(ref dualboot)) = (&mut pf, &migration.dual_boot_with) { + if let Ok(supplement) = patterns::load_supplement(dualboot) { + primary.extend(supplement); + } + } + pf } else { None }; diff --git a/src-tauri/src/patterns.rs b/src-tauri/src/patterns.rs index 61fd741..9d39d6d 100644 --- a/src-tauri/src/patterns.rs +++ b/src-tauri/src/patterns.rs @@ -87,6 +87,42 @@ pub fn load( anyhow::bail!("pattern file not found for {source_os}-to-{distro_family}.toml") } +impl PatternFile { + /// Merge patterns and log_paths from `other` into this file. + /// Used to layer a dual-boot supplement on top of the primary pattern file. + pub fn extend(&mut self, other: PatternFile) { + self.patterns.extend(other.patterns); + for (k, v) in other.log_paths { + self.log_paths.entry(k).or_insert(v); + } + } +} + +/// Load a supplementary pattern file by name (e.g. "windows" loads `dualboot-windows.toml`). +/// Supplement files cover coexistence-specific issues and are merged into the primary file. +pub fn load_supplement(name: &str) -> Result { + let filename = format!("dualboot-{name}.toml"); + let candidates = [ + format!("patterns/{filename}"), + format!("src-tauri/patterns/{filename}"), + format!("/usr/share/robin/patterns/{filename}"), + ]; + for path in &candidates { + let content = match std::fs::read_to_string(path) { + Ok(c) => c, + Err(_) => continue, + }; + match toml::from_str::(&content) { + Ok(pf) => return Ok(pf), + Err(e) => { + log::warn!("patterns: failed to parse supplement {path}: {e}"); + continue; + } + } + } + anyhow::bail!("supplement not found: {filename}") +} + #[must_use] pub fn classify(event: &SystemEvent, pf: &PatternFile) -> Option { for pattern in &pf.patterns { diff --git a/src/components/Onboarding.vue b/src/components/Onboarding.vue index 4215630..b212652 100644 --- a/src/components/Onboarding.vue +++ b/src/components/Onboarding.vue @@ -2,60 +2,150 @@
-

Hi, I'm Robin.

-

I help you find your feet on Linux. Before we start — what were you using before?

-
- + + + + +
@@ -70,7 +160,7 @@ async function detectDistro(): Promise { } .onboarding-card { - max-width: 320px; + max-width: 340px; text-align: center; } @@ -95,6 +185,7 @@ p { color: #aaa; line-height: 1.5; margin-bottom: 20px; } color: #e0e0e0; cursor: pointer; transition: border-color 0.15s, background 0.15s; + text-align: left; } .os-btn:hover { border-color: #6c8ebf; }