Commit 0c0f0a28a0ab26ed032fbedae1f793f78dad22ba

Commits
[COMMIT BEGIN]
commit 0c0f0a28a0ab26ed032fbedae1f793f78dad22ba
Author: 0x4248 <[email protected]>
Date:   Sun Mar 15 18:49:03 2026 +0000

    felixmenu: init

diff --git a/systems/linux/dotfiles/felix/home/felix/felixmenu/dialog.conf b/systems/linux/dotfiles/felix/home/felix/felixmenu/dialog.conf
new file mode 100644
index 0000000..5529740
--- /dev/null
+++ b/systems/linux/dotfiles/felix/home/felix/felixmenu/dialog.conf
@@ -0,0 +1,42 @@
+aspect = 0
+separate_widget = ""
+tab_len = 0
+visit_items = OFF
+use_shadow = OFF
+use_colors = ON
+screen_color = (WHITE,BLACK,OFF)
+dialog_color = (WHITE,BLACK,OFF)
+title_color = (WHITE,BLACK,OFF)
+border_color = (WHITE,BLACK,OFF)
+border2_color = (WHITE,BLACK,OFF)
+button_active_color = (BLACK,CYAN,OFF)
+button_inactive_color = (WHITE,BLACK,OFF)
+button_key_active_color = (BLACK,CYAN,OFF)
+button_key_inactive_color = (CYAN,BLACK,OFF)
+button_label_active_color = (BLACK,CYAN,OFF)
+button_label_inactive_color = (WHITE,BLACK,OFF)
+menubox_color = (WHITE,BLACK,OFF)
+menubox_border_color = (WHITE,BLACK,OFF)
+menubox_border2_color = (WHITE,BLACK,OFF)
+item_color = (WHITE,BLACK,OFF)
+item_selected_color = (BLACK,CYAN,OFF)
+tag_color = (CYAN,BLACK,OFF)
+tag_selected_color = (BLACK,CYAN,OFF)
+tag_key_color = (CYAN,BLACK,OFF)
+tag_key_selected_color = (BLACK,CYAN,OFF)
+inputbox_color = (WHITE,BLACK,OFF)
+inputbox_border_color = (WHITE,BLACK,OFF)
+inputbox_border2_color = (WHITE,BLACK,OFF)
+check_color = (WHITE,BLACK,OFF)
+check_selected_color = (BLACK,CYAN,OFF)
+uarrow_color = (WHITE,BLACK,OFF)
+darrow_color = (WHITE,BLACK,OFF)
+searchbox_color = (WHITE,BLACK,OFF)
+searchbox_title_color = (WHITE,BLACK,OFF)
+searchbox_border_color = (WHITE,BLACK,OFF)
+searchbox_border2_color = (WHITE,BLACK,OFF)
+position_indicator_color = (WHITE,BLACK,OFF)
+gauge_color = (BLACK,CYAN,OFF)
+form_active_text_color = (BLACK,CYAN,OFF)
+form_text_color = (WHITE,BLACK,OFF)
+form_item_readonly_color = (WHITE,BLACK,OFF)
diff --git a/systems/linux/dotfiles/felix/home/felix/felixmenu/dialog_menu.py b/systems/linux/dotfiles/felix/home/felix/felixmenu/dialog_menu.py
new file mode 100644
index 0000000..beead35
--- /dev/null
+++ b/systems/linux/dotfiles/felix/home/felix/felixmenu/dialog_menu.py
@@ -0,0 +1,157 @@
+#!/usr/bin/env python3
+"""
+Felix Menu - a dialog(1)-based launcher.
+Requires the `dialog` binary (Debian/Ubuntu).
+Usage: ./dialog_menu.py [menu.json]
+Menu format: JSON list of items where each item is:
+  {"title": "Name", "cmd": "sh command", "help": "text", "submenu": [ ... ]}
+
+Keys:
+  Enter  - select item / run command
+  Help   - show help text for the highlighted item (if any)
+  Esc    - go back / exit
+"""
+
+import json
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+
+APP_TITLE = 'Felix Menu'
+GREEN = '\033[32m'
+RED   = '\033[31m'
+RESET = '\033[0m'
+
+
+def setup_theme():
+    conf = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'dialog.conf')
+    if not os.path.exists(conf):
+        sys.exit(f'Error: theme file not found: {conf}')
+    os.environ['DIALOGRC'] = conf
+
+
+def check_dialog():
+    if shutil.which('dialog') is None:
+        print('dialog binary not found. Install with: sudo apt install dialog')
+        sys.exit(1)
+
+
+def load_menu(path):
+    with open(path, 'r', encoding='utf-8') as f:
+        return json.load(f)
+
+
+def item_label(it):
+    base = it.get('title') or it.get('label') or '(no title)'
+    if 'submenu' in it:
+        return base + '  >'
+    return base
+
+
+def dialog_menu(title, items):
+    tags = []
+    mapping = {}
+    for i, it in enumerate(items, 1):
+        tag = str(i)
+        tags.extend([tag, item_label(it)])
+        mapping[tag] = it
+
+    height      = max(10, min(20, len(items) + 6))
+    width       = 64
+    list_height = min(len(items), height - 6)
+
+    fd, out_path = tempfile.mkstemp(prefix='felix_choice_')
+    os.close(fd)
+
+    try:
+        cmd = [
+            'dialog',
+            '--output-fd', '1',
+            '--extra-button', '--extra-label', 'Help',
+            '--ok-label', 'Select',
+            '--cancel-label', 'Back',
+            '--menu', title,
+            str(height), str(width), str(list_height),
+        ] + tags
+
+        with open(out_path, 'w') as out_f, open('/dev/tty', 'r') as tty_in:
+            proc = subprocess.run(cmd, stdin=tty_in, stdout=out_f, stderr=None)
+
+        with open(out_path, 'r') as out_f:
+            choice = out_f.read().strip()
+
+    finally:
+        try:
+            os.unlink(out_path)
+        except OSError:
+            pass
+
+    return proc.returncode, choice, mapping
+
+
+def run_menu(items, title=APP_TITLE):
+    while True:
+        rc, choice, mapping = dialog_menu(title, items)
+
+        if rc == 1 or rc == 255:
+            return
+
+        sel = mapping.get(choice)
+        if sel is None:
+            return
+
+        if rc == 3:
+            help_text = sel.get('help', '').strip()
+            if help_text:
+                show_msg(title='Help - ' + (sel.get('title') or ''), text=help_text)
+            else:
+                show_msg(title='Help', text='No help available for this item.')
+            continue
+
+        if 'submenu' in sel:
+            run_menu(sel['submenu'], title=sel.get('title', APP_TITLE))
+            continue
+
+        if 'cmd' not in sel and 'help' in sel:
+            show_msg(
+                title=sel.get('title', 'Info'),
+                text='Press the Help button to read its description.',
+            )
+            continue
+
+        cmd_str = sel.get('cmd')
+        if cmd_str:
+            subprocess.run(['clear'])
+            result = subprocess.run(cmd_str, shell=True)
+            print()
+            if result.returncode == 0:
+                print(GREEN + 'Command finished successfully.' + RESET)
+            else:
+                print(RED + f'Command exited with code {result.returncode}.' + RESET)
+            input('\nPress Enter to return to Felix Menu...')
+
+
+def show_msg(title='Info', text=''):
+    with open('/dev/tty', 'r') as tty_in:
+        subprocess.run(['dialog', '--msgbox', text, '12', '64'], stdin=tty_in, stderr=None)
+
+
+def main():
+    check_dialog()
+    setup_theme()
+    path = sys.argv[1] if len(sys.argv) > 1 else 'menu.json'
+    if not os.path.exists(path):
+        print('menu file not found:', path)
+        sys.exit(2)
+    try:
+        menu = load_menu(path)
+    except Exception as e:
+        print('Failed to load menu:', e)
+        sys.exit(1)
+    run_menu(menu, title=APP_TITLE)
+
+
+if __name__ == '__main__':
+    main()
\ No newline at end of file
diff --git a/systems/linux/dotfiles/felix/home/felix/felixmenu/menu.json b/systems/linux/dotfiles/felix/home/felix/felixmenu/menu.json
new file mode 100644
index 0000000..5b2d6c4
--- /dev/null
+++ b/systems/linux/dotfiles/felix/home/felix/felixmenu/menu.json
@@ -0,0 +1,272 @@
+[
+  {
+    "title": "System",
+    "help": "System information and monitoring tools.",
+    "submenu": [
+      {
+        "title": "btop",
+        "cmd": "btop",
+        "help": "Interactive resource monitor. CPU, memory, disk, network at a glance."
+      },
+      {
+        "title": "htop",
+        "cmd": "htop",
+        "help": "Classic interactive process viewer. Kill, renice, and filter processes."
+      },
+      {
+        "title": "CPU info",
+        "cmd": "lscpu | less",
+        "help": "Show CPU architecture, cores, cache, and frequency details."
+      },
+      {
+        "title": "Memory usage",
+        "cmd": "free -h && echo '' && vmstat -s | head -20",
+        "help": "Show RAM and swap usage in human-readable form plus vmstat summary."
+      },
+      {
+        "title": "Hardware summary",
+        "cmd": "inxi -Fxz | less",
+        "help": "Full hardware overview: CPU, RAM, disks, network, audio. Requires inxi."
+      },
+      {
+        "title": "Uptime & load",
+        "cmd": "uptime && echo '' && w",
+        "help": "System uptime, load averages, and logged-in users."
+      },
+      {
+        "title": "Temperatures",
+        "cmd": "sensors",
+        "help": "CPU and motherboard temperatures via lm-sensors. Run sensors-detect first if blank."
+      },
+      {
+        "title": "USB devices",
+        "cmd": "lsusb -v 2>/dev/null | less",
+        "help": "List all connected USB devices with verbose details."
+      },
+      {
+        "title": "PCI devices",
+        "cmd": "lspci -v | less",
+        "help": "List PCI/PCIe devices: graphics card, sound, network controllers etc."
+      }
+    ]
+  },
+  {
+    "title": "Network",
+    "help": "Network status, configuration, and diagnostic tools.",
+    "submenu": [
+      {
+        "title": "nmtui",
+        "cmd": "nmtui",
+        "help": "NetworkManager text UI. Connect to wifi, edit connections, set hostname."
+      },
+      {
+        "title": "Connection status",
+        "cmd": "nmcli device status && echo '' && nmcli connection show --active",
+        "help": "Show all network devices and currently active connections."
+      },
+      {
+        "title": "IP addresses",
+        "cmd": "ip -c addr show | less",
+        "help": "Show all network interfaces and their assigned IP addresses."
+      },
+      {
+        "title": "Routing table",
+        "cmd": "ip route show",
+        "help": "Display the kernel routing table."
+      },
+      {
+        "title": "DNS lookup",
+        "cmd": "read -p 'Host to resolve: ' h && dig +short $h && echo '' && dig +short -x $(dig +short $h | head -1)",
+        "help": "Forward and reverse DNS lookup for any hostname."
+      },
+      {
+        "title": "Ping gateway",
+        "cmd": "ping -c 5 $(ip route | awk '/default/ {print $3; exit}')",
+        "help": "Ping your default gateway 5 times to check local connectivity."
+      },
+      {
+        "title": "Open ports",
+        "cmd": "ss -tulnp | less",
+        "help": "List all listening TCP and UDP ports with the process using them."
+      },
+      {
+        "title": "Bandwidth monitor",
+        "cmd": "bmon",
+        "help": "Real-time per-interface bandwidth monitor. Requires bmon."
+      },
+      {
+        "title": "Trace route",
+        "cmd": "read -p 'Target host: ' h && traceroute $h",
+        "help": "Trace the network path to a remote host hop by hop."
+      },
+      {
+        "title": "Firewall status",
+        "cmd": "sudo ufw status verbose",
+        "help": "Show current UFW firewall rules and status."
+      }
+    ]
+  },
+  {
+    "title": "Disk & Files",
+    "help": "Disk usage, filesystem, and file management tools.",
+    "submenu": [
+      {
+        "title": "Disk usage (ncdu)",
+        "cmd": "ncdu /",
+        "help": "Interactive disk usage browser. Navigate with arrows, delete with d. Requires ncdu."
+      },
+      {
+        "title": "Filesystem overview",
+        "cmd": "df -hT | less",
+        "help": "Show all mounted filesystems, their type, size, used, and available space."
+      },
+      {
+        "title": "Block devices",
+        "cmd": "lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,LABEL",
+        "help": "Tree view of all block devices: disks, partitions, loop devices."
+      },
+      {
+        "title": "SMART status",
+        "cmd": "lsblk -dno NAME | grep -E 'sd|nvme' | while read d; do echo \"--- /dev/$d ---\"; sudo smartctl -H /dev/$d; done",
+        "help": "Check SMART health status for all drives. Requires smartmontools."
+      },
+      {
+        "title": "Mount a device",
+        "cmd": "lsblk && echo '' && read -p 'Device (e.g. sdb1): ' d && read -p 'Mount point: ' m && sudo mount /dev/$d $m && echo Mounted.",
+        "help": "List block devices then mount a chosen device to a path."
+      },
+      {
+        "title": "Unmount a device",
+        "cmd": "lsblk && echo '' && read -p 'Mount point to unmount: ' m && sudo umount $m && echo Done.",
+        "help": "Unmount a filesystem by its mount point."
+      },
+      {
+        "title": "Find large files",
+        "cmd": "read -p 'Search under (default /home): ' d && find ${d:-/home} -type f -printf '%s %p\\n' 2>/dev/null | sort -rn | head -30 | awk '{printf \"%-10s %s\\n\", $1/1024/1024 \"MB\", $2}' | less",
+        "help": "Find the 30 largest files under a given directory."
+      },
+      {
+        "title": "Midnight Commander",
+        "cmd": "mc",
+        "help": "Two-pane file manager with built-in viewer and editor. Requires mc."
+      }
+    ]
+  },
+  {
+    "title": "Packages",
+    "help": "APT package management shortcuts.",
+    "submenu": [
+      {
+        "title": "Update package list",
+        "cmd": "sudo apt update",
+        "help": "Refresh the APT package index from all configured sources."
+      },
+      {
+        "title": "Upgrade all packages",
+        "cmd": "sudo apt update && sudo apt upgrade",
+        "help": "Update package list then upgrade all installed packages."
+      },
+      {
+        "title": "Full upgrade",
+        "cmd": "sudo apt update && sudo apt full-upgrade",
+        "help": "Like upgrade but also handles changing dependencies and removes obsolete packages."
+      },
+      {
+        "title": "Install a package",
+        "cmd": "read -p 'Package name: ' p && sudo apt install $p",
+        "help": "Install a package by name."
+      },
+      {
+        "title": "Remove a package",
+        "cmd": "read -p 'Package to remove: ' p && sudo apt remove $p",
+        "help": "Remove a package but keep its configuration files."
+      },
+      {
+        "title": "Purge a package",
+        "cmd": "read -p 'Package to purge: ' p && sudo apt purge $p",
+        "help": "Remove a package and delete its configuration files."
+      },
+      {
+        "title": "Search packages",
+        "cmd": "read -p 'Search term: ' q && apt-cache search $q | less",
+        "help": "Search APT for packages matching a keyword."
+      },
+      {
+        "title": "Show package info",
+        "cmd": "read -p 'Package name: ' p && apt-cache show $p | less",
+        "help": "Display detailed info about a package: version, deps, description."
+      },
+      {
+        "title": "List installed",
+        "cmd": "dpkg --get-selections | grep -v deinstall | less",
+        "help": "List all currently installed packages."
+      },
+      {
+        "title": "Autoremove",
+        "cmd": "sudo apt autoremove",
+        "help": "Remove packages that were installed as dependencies but are no longer needed."
+      },
+      {
+        "title": "Clean cache",
+        "cmd": "sudo apt clean && sudo apt autoclean",
+        "help": "Delete downloaded package files from the APT cache to free disk space."
+      }
+    ]
+  },
+  {
+    "title": "Logs & Diagnostics",
+    "help": "System logs, journal, and diagnostic tools.",
+    "submenu": [
+      {
+        "title": "journalctl (live)",
+        "cmd": "journalctl -f --output=short-precise",
+        "help": "Follow the systemd journal live. Shows new log entries as they arrive."
+      },
+      {
+        "title": "journalctl (boot)",
+        "cmd": "journalctl -b --output=short-precise | less",
+        "help": "Show all log entries from the current boot."
+      },
+      {
+        "title": "journalctl (errors)",
+        "cmd": "journalctl -b -p err --output=short-precise | less",
+        "help": "Show only error-level and above messages from the current boot."
+      },
+      {
+        "title": "Previous boot log",
+        "cmd": "journalctl -b -1 --output=short-precise | less",
+        "help": "Show journal from the previous boot. Useful after a crash or unexpected reboot."
+      },
+      {
+        "title": "Kernel ring buffer",
+        "cmd": "dmesg --color=never -T | less",
+        "help": "Show kernel messages with human-readable timestamps. Good for hardware issues."
+      },
+      {
+        "title": "Failed services",
+        "cmd": "systemctl --failed",
+        "help": "List all systemd units that are currently in a failed state."
+      },
+      {
+        "title": "Service status",
+        "cmd": "read -p 'Service name: ' s && systemctl status $s | less",
+        "help": "Check the status and recent log output of any systemd service."
+      },
+      {
+        "title": "Auth log",
+        "cmd": "sudo journalctl -u ssh -b | less",
+        "help": "Show SSH login attempts and authentication events from this boot."
+      },
+      {
+        "title": "Disk errors",
+        "cmd": "sudo journalctl -b -k | grep -iE 'error|fail|bad sector|ata' | less",
+        "help": "Filter kernel journal for disk-related errors and ATA messages."
+      },
+      {
+        "title": "Last logins",
+        "cmd": "last | head -30",
+        "help": "Show the 30 most recent user logins and reboots."
+      }
+    ]
+  }
+]
\ No newline at end of file
[COMMIT END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.