feat: Enhance terminal compatibility and add Arch Linux support

- Updated terminal.py to support Arch Linux alongside Debian-based systems.
- Implemented distro detection and user override for unsupported distributions.
- Added a status bar feature with customizable colors and live updates.
- Introduced JSON-based user preferences for status bar color.
- Replaced neofetch with fastfetch for system info display.
- Updated version to 0.1.2.a in version.txt.
- Created a new website with installation instructions, features, and community engagement.
- Added JavaScript for interactive elements and CSS for styling the website.
This commit is contained in:
2025-11-12 21:59:23 -05:00
parent 95d01c6897
commit 2e67031175
7 changed files with 1069 additions and 67 deletions

Binary file not shown.

95
install.sh Normal file → Executable file
View File

@@ -72,20 +72,52 @@ if [ -f "$BIN_DIR/zdtt" ] || [ -d "$INSTALL_DIR" ]; then
esac esac
fi fi
# Check if running on Debian-based Linux # Check if running on a supported Linux distribution
IS_DEBIAN=false IS_DEBIAN=false
IS_ARCH=false
DETECTED_DISTRO="other"
OS_ID=""
OS_LIKE=""
if [ -f /etc/os-release ]; then
# shellcheck disable=SC1091
. /etc/os-release
OS_ID=$(echo "${ID:-}" | tr '[:upper:]' '[:lower:]')
OS_LIKE=$(echo "${ID_LIKE:-}" | tr '[:upper:]' '[:lower:]')
fi
if [ -f /etc/debian_version ]; then if [ -f /etc/debian_version ]; then
IS_DEBIAN=true IS_DEBIAN=true
DETECTED_DISTRO="debian"
echo -e "${GREEN}${NC} Debian-based Linux detected" echo -e "${GREEN}${NC} Debian-based Linux detected"
elif [ -f /etc/arch-release ] || [ -f /etc/artix-release ]; then
IS_ARCH=true
DETECTED_DISTRO="arch"
echo -e "${GREEN}${NC} Arch Linux detected"
elif [[ "$OS_ID" == "debian" || "$OS_ID" == "ubuntu" || "$OS_ID" == "linuxmint" || "$OS_ID" == "pop" || "$OS_ID" == "pop-os" || "$OS_ID" == "pop_os" || "$OS_ID" == "elementary" || "$OS_LIKE" == *"debian"* || "$OS_LIKE" == *"ubuntu"* ]]; then
IS_DEBIAN=true
DETECTED_DISTRO="debian"
echo -e "${GREEN}${NC} Debian-based Linux detected (via os-release)"
elif [[ "$OS_ID" == "arch" || "$OS_ID" == "archlinux" || "$OS_ID" == "manjaro" || "$OS_ID" == "endeavouros" || "$OS_ID" == "endeavour" || "$OS_ID" == "arcolinux" || "$OS_ID" == "garuda" || "$OS_ID" == "artix" || "$OS_ID" == "blackarch" || "$OS_LIKE" == *"arch"* ]]; then
IS_ARCH=true
DETECTED_DISTRO="arch"
echo -e "${GREEN}${NC} Arch-based Linux detected (via os-release)"
elif command -v apt-get &>/dev/null; then
IS_DEBIAN=true
DETECTED_DISTRO="debian"
echo -e "${GREEN}${NC} Debian-based Linux detected (via package manager)"
elif command -v pacman &>/dev/null; then
IS_ARCH=true
DETECTED_DISTRO="arch"
echo -e "${GREEN}${NC} Arch-based Linux detected (via package manager)"
else else
echo -e "${YELLOW}${NC} Non-Debian distribution detected" echo -e "${YELLOW}${NC} Unsupported distribution detected"
echo "" echo ""
echo "ZDTT Terminal is optimized for Debian-based systems." echo "ZDTT Terminal is optimized for Debian-based and Arch Linux systems."
echo "(Debian, Ubuntu, Linux Mint, Pop!_OS, etc.)"
echo "" echo ""
echo "Running on a non-Debian system may result in:" echo "Running on an unsupported system may result in:"
echo " • Some commands may not work as expected" echo " • Some commands may not work as expected"
echo " • Auto-install features (like neofetch) will not work" echo " • Auto-install features (like fastfetch) will not work"
echo " • Reduced plugin compatibility" echo " • Reduced plugin compatibility"
echo " • Package management commands unavailable" echo " • Package management commands unavailable"
echo "" echo ""
@@ -100,6 +132,37 @@ else
fi fi
fi fi
echo "Detected distribution: ${DETECTED_DISTRO}"
read -p "Override detection? (debian/arch/other, Enter to keep): " -r USER_OVERRIDE
USER_OVERRIDE=$(echo "$USER_OVERRIDE" | tr '[:upper:]' '[:lower:]')
case "$USER_OVERRIDE" in
debian)
IS_DEBIAN=true
IS_ARCH=false
DETECTED_DISTRO="debian"
echo "Override applied: Debian-based system selected."
;;
arch)
IS_DEBIAN=false
IS_ARCH=true
DETECTED_DISTRO="arch"
echo "Override applied: Arch-based system selected."
;;
other)
IS_DEBIAN=false
IS_ARCH=false
DETECTED_DISTRO="other"
echo "Override applied: Unsupported/Other selected."
;;
"")
echo "Keeping detected distribution."
;;
*)
echo "Unknown override '$USER_OVERRIDE'. Keeping detected distribution."
;;
esac
# Check if Python 3 is installed # Check if Python 3 is installed
if ! command -v python3 &> /dev/null; then if ! command -v python3 &> /dev/null; then
echo -e "${RED}${NC} Python 3 is not installed" echo -e "${RED}${NC} Python 3 is not installed"
@@ -121,11 +184,28 @@ if ! command -v python3 &> /dev/null; then
exit 1 exit 1
fi fi
echo -e "${GREEN}${NC} Python 3 installed successfully"
elif [ "$IS_ARCH" = true ]; then
echo "Installing Python 3..."
# Sync package databases and install Python
sudo pacman -Sy --noconfirm python
if [ $? -ne 0 ]; then
echo -e "${RED}Failed to install Python 3${NC}"
echo "Please install Python 3 manually: sudo pacman -S python"
echo ""
echo "Press any key to exit..."
read -n 1 -s -r
exit 1
fi
echo -e "${GREEN}${NC} Python 3 installed successfully" echo -e "${GREEN}${NC} Python 3 installed successfully"
else else
echo -e "${RED}Python 3 is required but auto-install is not supported on non-Debian systems.${NC}" echo -e "${RED}Python 3 is required but auto-install is not supported on this distribution.${NC}"
echo "" echo ""
echo "Please install Python 3 manually using your package manager:" echo "Please install Python 3 manually using your package manager:"
echo " • Debian/Ubuntu: sudo apt-get install python3"
echo " • Arch/Manjaro: sudo pacman -S python" echo " • Arch/Manjaro: sudo pacman -S python"
echo " • Fedora: sudo dnf install python3" echo " • Fedora: sudo dnf install python3"
echo " • openSUSE: sudo zypper install python3" echo " • openSUSE: sudo zypper install python3"
@@ -435,4 +515,3 @@ echo " zdtt start"
echo "" echo ""
echo "Press any key to exit..." echo "Press any key to exit..."
read -n 1 -s -r read -n 1 -s -r

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
ZDTT Terminal - A custom terminal interface ZDTT Terminal - A custom terminal interface
Only works on Debian-based Linux systems Only works on Debian-based or Arch Linux systems
""" """
import os import os
@@ -13,14 +13,147 @@ import readline
import glob import glob
import atexit import atexit
import logging import logging
import threading
import json
from datetime import datetime from datetime import datetime
import urllib.request import urllib.request
import urllib.error import urllib.error
import time as time_module import time as time_module
SUPPORTED_DEBIAN_IDS = {
'debian',
'ubuntu',
'linuxmint',
'mint',
'pop',
'pop-os',
'pop_os',
'elementary',
'zorin',
'kali',
'parrot',
'mx',
'mx-linux',
'deepin',
'peppermint',
'raspbian',
'neon',
}
SUPPORTED_ARCH_IDS = {
'arch',
'archlinux',
'manjaro',
'endeavouros',
'endeavour',
'arcolinux',
'garuda',
'artix',
'blackarch',
'chakra',
}
STATUS_BAR_COLORS = {
'blue': ('44', '97'),
'red': ('41', '97'),
'green': ('42', '30'),
'cyan': ('46', '30'),
'magenta': ('45', '97'),
'yellow': ('43', '30'),
'white': ('47', '30'),
'black': ('40', '97'),
}
def _parse_os_release():
"""Return a dict of fields from /etc/os-release if available"""
data = {}
try:
with open('/etc/os-release', 'r') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#') or '=' not in line:
continue
key, value = line.split('=', 1)
value = value.strip().strip('"')
data[key] = value
except FileNotFoundError:
pass
return data
def _collect_tokens(*values):
"""Normalize distro identifiers for comparison"""
tokens = set()
for value in values:
if not value:
continue
normalized = value.replace('"', '').strip().lower()
if not normalized:
continue
# Keep the raw normalized value plus its dashed/underscored variants split
tokens.add(normalized)
delimiters_replaced = normalized.replace('-', ' ').replace('_', ' ')
for part in delimiters_replaced.split():
if part:
tokens.add(part)
return tokens
def _detect_supported_distro():
"""Return distro identifier: 'debian', 'arch', or 'other'"""
if os.path.exists('/etc/debian_version'):
return 'debian'
arch_markers = (
'/etc/arch-release',
'/etc/artix-release',
)
if any(os.path.exists(path) for path in arch_markers):
return 'arch'
os_release = _parse_os_release()
tokens = _collect_tokens(os_release.get('ID'), os_release.get('ID_LIKE'))
if tokens & SUPPORTED_DEBIAN_IDS:
return 'debian'
if tokens & SUPPORTED_ARCH_IDS:
return 'arch'
# Fallback to package manager detection
if shutil.which('apt-get'):
return 'debian'
if shutil.which('pacman'):
return 'arch'
return 'other'
def _prompt_distro_override(detected_distro):
"""Allow user to override detected distro."""
label_map = {
'debian': "Debian-based",
'arch': "Arch-based",
'other': "Unsupported/Other",
}
print("=" * 60)
print(f"Detected distribution: {label_map.get(detected_distro, 'Unknown')}")
print("If this is incorrect, enter one of: debian / arch / other.")
print("Press Enter to accept the detected value.")
override = input("Override distribution (leave blank to keep): ").strip().lower()
if override in ('debian', 'arch', 'other'):
return override
if override:
print(f"Unknown override '{override}'. Using detected value.")
return detected_distro
def check_system_compatibility(): def check_system_compatibility():
"""Check system compatibility and warn if not Debian-based""" """Detect supported distributions and warn when unsupported"""
# Check if running on Linux # Check if running on Linux
if sys.platform != 'linux': if sys.platform != 'linux':
print("=" * 60) print("=" * 60)
@@ -34,19 +167,21 @@ def check_system_compatibility():
if response != 'yes': if response != 'yes':
print("Installation cancelled.") print("Installation cancelled.")
sys.exit(0) sys.exit(0)
return False return 'other'
# Check for Debian-specific file # Detect supported distributions
if not os.path.exists('/etc/debian_version'): distro = _detect_supported_distro()
if distro not in ('debian', 'arch'):
# Unsupported distribution
print("=" * 60) print("=" * 60)
print("⚠️ WARNING: Non-Debian Distribution Detected") print("⚠️ WARNING: Unsupported Distribution Detected")
print("=" * 60) print("=" * 60)
print("ZDTT Terminal is optimized for Debian-based systems.") print("ZDTT Terminal is optimized for Debian-based and Arch Linux systems.")
print("(Debian, Ubuntu, Linux Mint, Pop!_OS, etc.)")
print() print()
print("Running on a non-Debian system may result in:") print("Running on your current system may result in:")
print(" • Some commands may not work as expected") print(" • Some commands may not work as expected")
print(" • Auto-install features (like neofetch) may fail") print(" • Auto-install features may fail")
print(" • Reduced plugin compatibility") print(" • Reduced plugin compatibility")
print(" • Package management commands unavailable") print(" • Package management commands unavailable")
print() print()
@@ -54,24 +189,32 @@ def check_system_compatibility():
if response != 'yes': if response != 'yes':
print("Installation cancelled.") print("Installation cancelled.")
sys.exit(0) sys.exit(0)
return False
# It's a Debian-based system # Offer override regardless of detection
return True distro = _prompt_distro_override(distro)
return distro
class ZDTTTerminal: class ZDTTTerminal:
def __init__(self, is_debian=True): def __init__(self, distro='debian'):
self.username = getpass.getuser() self.username = getpass.getuser()
self.running = True self.running = True
self.current_dir = os.getcwd() self.current_dir = os.getcwd()
self.is_debian = is_debian self.distro = distro
self.is_debian = distro == 'debian'
self.is_arch = distro == 'arch'
self.is_supported = self.is_debian or self.is_arch
self.zdtt_dir = os.path.expanduser("~/.zdtt") self.zdtt_dir = os.path.expanduser("~/.zdtt")
self.history_file = os.path.expanduser("~/.zdtt_history") self.history_file = os.path.expanduser("~/.zdtt_history")
self.plugin_dir = os.path.join(self.zdtt_dir, "plugins") self.plugin_dir = os.path.join(self.zdtt_dir, "plugins")
self.log_file = os.path.join(self.zdtt_dir, "plugin_errors.log") self.log_file = os.path.join(self.zdtt_dir, "plugin_errors.log")
self.banner_file = os.path.join(self.zdtt_dir, "banner.txt") self.banner_file = os.path.join(self.zdtt_dir, "banner.txt")
self.aliases_file = os.path.join(self.zdtt_dir, "aliases") self.aliases_file = os.path.join(self.zdtt_dir, "aliases")
self.config_file = os.path.join(self.zdtt_dir, "config.json")
self.status_bar_color = 'blue'
self.status_bar_thread = None
self.status_bar_stop_event = threading.Event()
self.scroll_region_set = False
# Setup logging for plugins # Setup logging for plugins
self.setup_logging() self.setup_logging()
@@ -83,6 +226,9 @@ class ZDTTTerminal:
# Read version from version.txt # Read version from version.txt
self.version = self.read_version() self.version = self.read_version()
# Load user preferences (status bar color, etc.)
self.load_preferences()
# ANSI color codes # ANSI color codes
self.COLOR_RESET = '\033[0m' self.COLOR_RESET = '\033[0m'
self.COLOR_GREEN = '\033[92m' self.COLOR_GREEN = '\033[92m'
@@ -103,13 +249,14 @@ class ZDTTTerminal:
'unalias': self.cmd_unalias, 'unalias': self.cmd_unalias,
'zps': self.cmd_zps, 'zps': self.cmd_zps,
'time': self.cmd_time, 'time': self.cmd_time,
'statusbar': self.cmd_statusbar,
# System commands # System commands
'ls': self.cmd_ls, 'ls': self.cmd_ls,
'pwd': self.cmd_pwd, 'pwd': self.cmd_pwd,
'cd': self.cmd_cd, 'cd': self.cmd_cd,
'cat': self.cmd_cat, 'cat': self.cmd_cat,
'nano': self.cmd_nano, 'nano': self.cmd_nano,
'neofetch': self.cmd_neofetch, 'fastfetch': self.cmd_fastfetch,
'mkdir': self.cmd_mkdir, 'mkdir': self.cmd_mkdir,
'touch': self.cmd_touch, 'touch': self.cmd_touch,
'rm': self.cmd_rm, 'rm': self.cmd_rm,
@@ -166,6 +313,31 @@ class ZDTTTerminal:
# Fallback version if file not found # Fallback version if file not found
return "0.0.1.a" return "0.0.1.a"
def load_preferences(self):
"""Load user preferences such as status bar color."""
try:
with open(self.config_file, 'r') as f:
data = json.load(f)
self.status_bar_color = data.get('status_bar_color', self.status_bar_color)
except FileNotFoundError:
pass
except json.JSONDecodeError:
logging.warning("Preferences file is corrupted; using defaults.")
def save_preferences(self):
"""Persist user preferences."""
data = {}
try:
with open(self.config_file, 'r') as f:
data = json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
data = {}
data['status_bar_color'] = self.status_bar_color
with open(self.config_file, 'w') as f:
json.dump(data, f, indent=2)
def check_for_updates(self): def check_for_updates(self):
"""Check if a new version is available""" """Check if a new version is available"""
try: try:
@@ -186,19 +358,21 @@ class ZDTTTerminal:
def display_banner(self): def display_banner(self):
"""Display the ZDTT ASCII art banner (or custom banner if available)""" """Display the ZDTT ASCII art banner (or custom banner if available)"""
print()
# Check terminal size to see if banner will fit # Check terminal size to see if banner will fit
try: try:
term_size = shutil.get_terminal_size() term_size = shutil.get_terminal_size()
# Banner is 44 chars wide and 11 lines tall (including version) # Banner is 44 chars wide and 11 lines tall (including version)
# Add extra space for non-Debian warning if needed # Add extra space for compatibility warning if needed
min_height = 13 if not self.is_debian else 11 min_height = 13 if not self.is_supported else 11
min_width = 44 min_width = 44
if term_size.columns < min_width or term_size.lines < min_height: if term_size.columns < min_width or term_size.lines < min_height:
# Terminal too small, skip banner and just show minimal header # Terminal too small, skip banner and just show minimal header
print(f"ZDTT Terminal v{self.version}") print(f"ZDTT Terminal v{self.version}")
if not self.is_debian: if not self.is_supported:
print("⚠️ Non-Debian system - limited support") print("⚠️ Unsupported system - limited support")
print() print()
return return
except Exception: except Exception:
@@ -214,8 +388,8 @@ class ZDTTTerminal:
if '{version}' in custom_banner: if '{version}' in custom_banner:
custom_banner = custom_banner.replace('{version}', self.version) custom_banner = custom_banner.replace('{version}', self.version)
print(custom_banner) print(custom_banner)
# Show non-Debian warning # Show warning for unsupported systems
if not self.is_debian: if not self.is_supported:
self._show_compatibility_warning() self._show_compatibility_warning()
return return
except Exception as e: except Exception as e:
@@ -237,16 +411,105 @@ ZDTT Terminal v{self.version}
""" """
print(banner) print(banner)
# Show non-Debian warning after banner # Show warning for unsupported systems after banner
if not self.is_debian: if not self.is_supported:
self._show_compatibility_warning() self._show_compatibility_warning()
def _show_compatibility_warning(self): def _show_compatibility_warning(self):
"""Show compatibility warning for non-Debian systems""" """Show compatibility warning for unsupported systems"""
if self.is_supported:
return
print() print()
print("⚠️ Running on non-Debian system - limited support") print("⚠️ Running on unsupported system - limited support")
print(" Tested on Debian-based and Arch Linux distributions.")
print() print()
def initialize_status_bar(self):
"""Reserve the first terminal row and start the status bar thread."""
self._set_scroll_region()
self._start_status_bar_thread()
self._render_status_bar()
def shutdown_status_bar(self):
"""Stop the status bar thread and release terminal state."""
self.status_bar_stop_event.set()
if self.status_bar_thread and self.status_bar_thread.is_alive():
self.status_bar_thread.join(timeout=0.5)
self.status_bar_thread = None
self._reset_scroll_region()
def _start_status_bar_thread(self):
if self.status_bar_thread and self.status_bar_thread.is_alive():
return
self.status_bar_stop_event.clear()
self.status_bar_thread = threading.Thread(
target=self._status_bar_loop,
name="ZDTTStatusBar",
daemon=True,
)
self.status_bar_thread.start()
def _status_bar_loop(self):
while not self.status_bar_stop_event.is_set():
self._render_status_bar()
if self.status_bar_stop_event.wait(2):
break
def _render_status_bar(self):
"""Render a single-line status bar with branding and time."""
bar_text = self._build_status_bar_text()
try:
sys.stdout.write("\033[s") # Save cursor position
sys.stdout.write("\033[1;1H") # Move to first row
sys.stdout.write("\033[2K") # Clear the line
sys.stdout.write(bar_text)
sys.stdout.write(self.COLOR_RESET)
sys.stdout.write("\033[u") # Restore cursor
sys.stdout.flush()
except Exception:
print(bar_text)
def _build_status_bar_text(self):
left_text = "ZDTT by ZaneDev"
time_str = datetime.now().strftime("%I:%M:%p").lower()
try:
term_size = shutil.get_terminal_size()
width = max(term_size.columns, len(left_text) + len(time_str) + 4)
except Exception:
width = len(left_text) + len(time_str) + 4
padding = max(width - len(left_text) - len(time_str) - 2, 1)
bar_plain = f" {left_text}{' ' * padding}{time_str} "
if len(bar_plain) < width:
bar_plain = bar_plain.ljust(width)
else:
bar_plain = bar_plain[:width]
bg_code, fg_code = STATUS_BAR_COLORS.get(self.status_bar_color, ('44', '97'))
return f"\033[{bg_code}m\033[{fg_code}m{bar_plain}"
def _set_scroll_region(self):
"""Reserve the top row for the status bar."""
try:
rows = shutil.get_terminal_size().lines
rows = max(rows, 2)
sys.stdout.write(f"\033[2;{rows}r")
sys.stdout.write("\033[1;1H")
sys.stdout.write("\033[2K")
sys.stdout.write("\033[2;1H")
sys.stdout.flush()
self.scroll_region_set = True
except Exception:
self.scroll_region_set = False
def _reset_scroll_region(self):
"""Restore default scrolling behavior."""
if not self.scroll_region_set:
return
sys.stdout.write("\033[r")
sys.stdout.flush()
self.scroll_region_set = False
def setup_readline(self): def setup_readline(self):
"""Setup readline for history and tab completion""" """Setup readline for history and tab completion"""
# Setup history # Setup history
@@ -441,6 +704,7 @@ ZDTT Terminal v{self.version}
print(" unalias <name> - Remove an alias") print(" unalias <name> - Remove an alias")
print(" zps install <url> - Install plugin from URL") print(" zps install <url> - Install plugin from URL")
print(" time [options] - Display date/time (MM/DD/YY 12h default)") print(" time [options] - Display date/time (MM/DD/YY 12h default)")
print(" statusbar color <name> - Change status bar highlight color")
print(" exit - Exit ZDTT (return to shell)") print(" exit - Exit ZDTT (return to shell)")
print(" quit - Quit and close terminal window") print(" quit - Quit and close terminal window")
print() print()
@@ -461,7 +725,7 @@ ZDTT Terminal v{self.version}
print(" date - Display current date/time") print(" date - Display current date/time")
print(" uname [options] - Display system information") print(" uname [options] - Display system information")
print(" nano <file> - Edit file with nano") print(" nano <file> - Edit file with nano")
print(" neofetch - Display system info (auto-installs)") print(" fastfetch - Display system info (auto-installs)")
print() print()
print("Python Commands:") print("Python Commands:")
print(" python [args] - Run Python interpreter") print(" python [args] - Run Python interpreter")
@@ -479,6 +743,8 @@ ZDTT Terminal v{self.version}
def cmd_clear(self, args): def cmd_clear(self, args):
"""Clear the terminal screen""" """Clear the terminal screen"""
os.system('clear' if os.name != 'nt' else 'cls') os.system('clear' if os.name != 'nt' else 'cls')
self._set_scroll_region()
self._render_status_bar()
self.display_banner() self.display_banner()
def cmd_exit(self, args): def cmd_exit(self, args):
@@ -496,13 +762,15 @@ ZDTT Terminal v{self.version}
def cmd_about(self, args): def cmd_about(self, args):
"""Display information about ZDTT Terminal""" """Display information about ZDTT Terminal"""
print(f"\nZDTT Terminal v{self.version}") print(f"\nZDTT Terminal v{self.version}")
print("A custom terminal interface for Debian-based Linux") print("A custom terminal interface for Debian-based and Arch Linux systems")
# Show distribution status # Show distribution status
if self.is_debian: if self.is_debian:
print("Running on: Debian-based system (fully supported)") print("Running on: Debian-based system (fully supported)")
elif self.is_arch:
print("Running on: Arch Linux (fully supported)")
else: else:
print("Running on: Non-Debian system (limited support)") print("Running on: Unsupported system (limited support)")
print() print()
print("Features:") print("Features:")
@@ -815,6 +1083,35 @@ ZDTT Terminal v{self.version}
print(f"{date_str} {time_str}") print(f"{date_str} {time_str}")
def cmd_statusbar(self, args):
"""Configure the status bar appearance."""
if not args:
print(f"Status bar color: {self.status_bar_color}")
print("Usage: statusbar color <color>")
print(f"Available colors: {', '.join(sorted(STATUS_BAR_COLORS.keys()))}")
return
subcommand = args[0].lower()
if subcommand != 'color':
print("Unknown statusbar option. Usage: statusbar color <color>")
return
if len(args) < 2:
print("Missing color. Usage: statusbar color <color>")
print(f"Available colors: {', '.join(sorted(STATUS_BAR_COLORS.keys()))}")
return
color = args[1].lower()
if color not in STATUS_BAR_COLORS:
print(f"Unsupported color '{color}'.")
print(f"Available colors: {', '.join(sorted(STATUS_BAR_COLORS.keys()))}")
return
self.status_bar_color = color
self.save_preferences()
self._render_status_bar()
print(f"Status bar color updated to {color}.")
def cmd_echo(self, args): def cmd_echo(self, args):
"""Echo the provided arguments""" """Echo the provided arguments"""
if args: if args:
@@ -1034,32 +1331,97 @@ ZDTT Terminal v{self.version}
subprocess.run(['nano'] + args) subprocess.run(['nano'] + args)
def cmd_neofetch(self, args): def cmd_fastfetch(self, args):
"""Display system info with neofetch (auto-installs if needed)""" """Display system info with fastfetch (auto-installs if needed)"""
# Check if neofetch is installed def _find_fastfetch_binary():
if not shutil.which('neofetch'): """Return absolute path to fastfetch if available."""
if not self.is_debian: fastfetch_path = shutil.which('fastfetch')
print("neofetch is not installed.") if fastfetch_path:
print("Auto-install is only supported on Debian-based systems.") return fastfetch_path
print("Please install neofetch manually using your package manager:")
print(" • Arch/Manjaro: sudo pacman -S neofetch") # Fallback search in common locations
print(" • Fedora: sudo dnf install neofetch") common_paths = [
print(" • openSUSE: sudo zypper install neofetch") '/usr/bin/fastfetch',
'/usr/local/bin/fastfetch',
os.path.expanduser('~/.local/bin/fastfetch'),
]
for path in common_paths:
if os.path.isfile(path) and os.access(path, os.X_OK):
return path
return None
def _build_install_command():
"""Return (cmd_list, manual_hint) based on distro/privileges."""
manual_hint = None
if self.is_debian:
base_cmd = ['apt-get', 'install', '-y', 'fastfetch']
manual_hint = "sudo apt-get install fastfetch"
elif self.is_arch:
base_cmd = ['pacman', '-S', '--noconfirm', 'fastfetch']
manual_hint = "sudo pacman -S fastfetch"
else:
return None, None
# Determine if sudo is needed
geteuid = getattr(os, 'geteuid', None)
is_root = geteuid is not None and geteuid() == 0
sudo_path = shutil.which('sudo')
if is_root:
return base_cmd, manual_hint
if sudo_path:
return [sudo_path] + base_cmd, manual_hint
# Cannot elevate automatically
return None, manual_hint
# Check if fastfetch is installed
fastfetch_bin = _find_fastfetch_binary()
if not fastfetch_bin:
if not self.is_supported:
print("fastfetch is not installed.")
print("Auto-install is only supported on Debian-based and Arch Linux systems.")
print("Please install fastfetch manually using your package manager:")
print(" • Debian/Ubuntu: sudo apt-get install fastfetch")
print(" • Arch/Manjaro: sudo pacman -S fastfetch")
print(" • Fedora: sudo dnf install fastfetch")
print(" • openSUSE: sudo zypper install fastfetch")
return return
print("neofetch is not installed. Installing...") install_cmd, manual_hint = _build_install_command()
if not install_cmd:
print("fastfetch is not installed and cannot be auto-installed because elevated privileges")
print("are required but 'sudo' was not found (or you're not running as root).")
if manual_hint:
print(f"Try manually: {manual_hint}")
else:
print("Please install fastfetch via your package manager.")
return
print("fastfetch is not installed. Installing...")
print() print()
try: try:
subprocess.run(['sudo', 'apt-get', 'install', '-y', 'neofetch'], check=True) subprocess.run(install_cmd, check=True)
print() print()
print("neofetch installed successfully!") print("fastfetch installed successfully!")
print() print()
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
print("Failed to install neofetch") print("Failed to install fastfetch")
print("Try manually: sudo apt-get install neofetch") if manual_hint:
print(f"Try manually: {manual_hint}")
else:
print("Please install fastfetch via your package manager.")
return return
subprocess.run(['neofetch'] + args) fastfetch_bin = _find_fastfetch_binary()
if not fastfetch_bin:
print("fastfetch installation completed but binary was not found.")
print("Ensure fastfetch is in your PATH and try again.")
return
subprocess.run([fastfetch_bin] + args)
# Python Commands # Python Commands
@@ -1126,28 +1488,31 @@ ZDTT Terminal v{self.version}
"""Main terminal loop""" """Main terminal loop"""
# Clear screen and display banner # Clear screen and display banner
os.system('clear' if os.name != 'nt' else 'cls') os.system('clear' if os.name != 'nt' else 'cls')
self.initialize_status_bar()
self.display_banner() self.display_banner()
# Main command loop # Main command loop
while self.running: try:
try: while self.running:
command = input(self.get_prompt()) try:
self.execute_command(command) command = input(self.get_prompt())
except KeyboardInterrupt: self.execute_command(command)
print("\nUse 'exit' to return to shell, or 'quit' to close the window.") except KeyboardInterrupt:
except EOFError: print("\nUse 'exit' to return to shell, or 'quit' to close the window.")
print("\nGoodbye!") except EOFError:
break print("\nGoodbye!")
break
finally:
self.shutdown_status_bar()
def main(): def main():
# Check system compatibility # Check system compatibility
is_debian = check_system_compatibility() distro = check_system_compatibility()
terminal = ZDTTTerminal(is_debian=is_debian) terminal = ZDTTTerminal(distro=distro)
terminal.run() terminal.run()
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -1 +1 @@
0.1.1.r 0.1.2.a

173
website/index.html Normal file
View File

@@ -0,0 +1,173 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ZDTT Terminal</title>
<link rel="stylesheet" href="styles.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>
<body>
<header class="hero" id="top">
<nav class="nav">
<div class="brand">ZDTT</div>
<button class="nav__toggle" aria-label="Toggle navigation">
<span></span>
<span></span>
<span></span>
</button>
<div class="nav__links">
<a href="#features">Features</a>
<a href="#install">Install</a>
<a href="#showcase">Showcase</a>
<a href="#community">Community</a>
</div>
</nav>
<div class="hero__content">
<p class="eyebrow">Linux-first terminal</p>
<h1>ZDTT Terminal</h1>
<p class="lead">
A vibe-coded, distro-aware terminal experience for Debian and Arch power users.
Smart onboarding, plugin support, custom aliases, and a living status bar—no fluff.
</p>
<div class="hero__actions">
<button class="btn primary" data-copy="#install-command">Copy Install Command</button>
<a class="btn ghost" href="#features">Explore Features</a>
</div>
<code id="install-command" class="install-command">
curl -O https://zdtt-sources.zane.org/install.sh && chmod +x install.sh && ./install.sh
</code>
</div>
<div class="hero__metrics">
<div class="metric">
<p class="metric__label">Current release</p>
<p class="metric__value">v0.1.2.a</p>
</div>
<div class="metric">
<p class="metric__label">Supported families</p>
<p class="metric__value">Debian · Arch</p>
</div>
<div class="metric">
<p class="metric__label">Extension slots</p>
<p class="metric__value">Plugins + Aliases</p>
</div>
</div>
</header>
<main>
<section id="features" class="section features">
<div class="section__intro">
<p class="eyebrow">Why ZDTT</p>
<h2>Built for Linux hackers, tuned for comfort.</h2>
<p>ZDTT pairs a friendly onboarding story with serious tooling. No learning cliff, no silent failure when you hop distros.</p>
</div>
<div class="feature-grid">
<article class="card">
<h3>Smart distro detection</h3>
<p>Installer inspects <code>/etc/os-release</code>, package managers, and user overrides to keep Debian and Arch flows tight.</p>
</article>
<article class="card">
<h3>Always-on status bar</h3>
<p>Background thread renders a live status bar with branding, time, and color themes you can swap on the fly.</p>
</article>
<article class="card">
<h3>Plugin-friendly core</h3>
<p>Drop Python hooks inside <code>~/.zdtt/plugins</code>, log issues automatically, and iterate without touching core files.</p>
</article>
<article class="card">
<h3>Aliases that stick</h3>
<p>Readable aliases file, inline history, and tab-completion support so your favorite shortcuts load every launch.</p>
</article>
<article class="card">
<h3>Command batteries included</h3>
<p><code>ls</code>, <code>grep</code>, <code>fastfetch</code>, Python runners, package helpers, and more are wired in.</p>
</article>
<article class="card">
<h3>Graceful fallbacks</h3>
<p>Unsupported systems get friendly warnings, opt-in installs, and clear messaging so nothing breaks silently.</p>
</article>
</div>
</section>
<section id="install" class="section install">
<div class="section__intro">
<p class="eyebrow">3-step install</p>
<h2>From curl to custom shell in minutes.</h2>
</div>
<ol class="install-steps">
<li>
<h3>Fetch the installer</h3>
<p>Use the one-liner above or download <code>install.sh</code> directly if you want to inspect the script first.</p>
</li>
<li>
<h3>Follow the prompts</h3>
<p>The script checks your distro, offers overrides, and handles both Debian (<code>apt</code>) and Arch (<code>pacman</code>) flows.</p>
</li>
<li>
<h3>Launch <code>zdtt</code></h3>
<p>Run <code>zdtt</code> from any shell. Tweak banner art, status-bar colors, and drop plugins into <code>~/.zdtt</code>.</p>
</li>
</ol>
<div class="callout">
<p><strong>Heads up:</strong> ZDTT is Linux-only. Non-Debian/Arch installs can continue at your own risk—warnings are built in.</p>
</div>
</section>
<section id="showcase" class="section showcase">
<div class="section__intro">
<p class="eyebrow">Showcase</p>
<h2>Personality baked in.</h2>
</div>
<div class="showcase__grid">
<figure>
<figcaption>Custom ASCII banner system</figcaption>
<pre>
░█████████ ░███████ ░██████████░██████████
░██ ░██ ░██ ░██ ░██
░██ ░██ ░██ ░██ ░██
░███ ░██ ░██ ░██ ░██
░██ ░██ ░██ ░██ ░██
░██ ░██ ░██ ░██ ░██
░█████████ ░███████ ░██ ░██
</pre>
</figure>
<figure>
<figcaption>Live status bar with themed colors</figcaption>
<div class="statusbar-demo">ZDTT by ZaneDev<span>10:24pm</span></div>
</figure>
<figure>
<figcaption>Plugin logging with guard rails</figcaption>
<pre>
2024-01-02 21:41:07 ERROR my_plugin
Traceback (most recent call last):
...
</pre>
</figure>
</div>
</section>
<section id="community" class="section community">
<div class="section__intro">
<p class="eyebrow">Community</p>
<h2>Bring your tweaks, aliases, and plugins.</h2>
<p>ZDTT thrives on experimentation. Fork it, vibe with it, file PRs, or just send screenshots.</p>
</div>
<div class="community__actions">
<a class="btn primary" href="mailto:hello@zane.org">Say hello</a>
<a class="btn ghost" href="https://github.com/" target="_blank" rel="noreferrer">View source</a>
</div>
</section>
</main>
<footer class="footer">
<p>© <span id="year"></span> ZDTT • Built for Debian + Arch explorers.</p>
<a href="#top">Back to top ↑</a>
</footer>
<script src="script.js"></script>
</body>
</html>

35
website/script.js Normal file
View File

@@ -0,0 +1,35 @@
document.addEventListener('DOMContentLoaded', () => {
const navToggle = document.querySelector('.nav__toggle');
const navLinks = document.querySelector('.nav__links');
const yearEl = document.getElementById('year');
if (yearEl) {
yearEl.textContent = new Date().getFullYear();
}
if (navToggle && navLinks) {
navToggle.addEventListener('click', () => {
navLinks.classList.toggle('is-open');
});
}
document.querySelectorAll('[data-copy]').forEach((button) => {
button.addEventListener('click', () => {
const target = document.querySelector(button.dataset.copy);
if (!target) {
return;
}
navigator.clipboard?.writeText(target.textContent.trim()).then(() => {
button.textContent = 'Copied!';
setTimeout(() => {
button.textContent = 'Copy Install Command';
}, 1800);
}).catch(() => {
button.textContent = 'Unable to copy';
setTimeout(() => {
button.textContent = 'Copy Install Command';
}, 1800);
});
});
});
});

350
website/styles.css Normal file
View File

@@ -0,0 +1,350 @@
:root {
color-scheme: dark;
font-family: 'Space Grotesk', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
--bg: #05060a;
--bg-alt: #0e1018;
--card: #121422;
--card-border: rgba(255, 255, 255, 0.08);
--text: #f5f6fd;
--muted: #9aa2c4;
--accent: #4dd5ff;
--accent-strong: #6d84ff;
--shadow: 0 18px 45px rgba(4, 6, 11, 0.6);
}
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
background: radial-gradient(circle at top, rgba(77, 213, 255, 0.12), transparent 55%),
radial-gradient(circle at 20% 20%, rgba(109, 132, 255, 0.25), transparent 35%),
var(--bg);
color: var(--text);
font-size: 1rem;
line-height: 1.6;
}
img {
max-width: 100%;
height: auto;
}
a {
color: inherit;
text-decoration: none;
}
.hero {
padding: 2.5rem clamp(1.5rem, 5vw, 5rem) 5rem;
display: flex;
flex-direction: column;
gap: 2rem;
}
.nav {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
}
.brand {
font-weight: 700;
letter-spacing: 0.08em;
}
.nav__links {
display: flex;
align-items: center;
gap: 1.5rem;
}
.nav__links a {
color: var(--muted);
font-weight: 500;
transition: color 0.2s ease;
}
.nav__links a:hover {
color: var(--text);
}
.nav__toggle {
display: none;
background: none;
border: none;
cursor: pointer;
gap: 0.4rem;
flex-direction: column;
}
.nav__toggle span {
display: block;
width: 1.5rem;
height: 2px;
background: var(--text);
}
.hero__content {
max-width: 48rem;
}
.eyebrow {
text-transform: uppercase;
letter-spacing: 0.3em;
font-size: 0.75rem;
color: var(--accent);
margin-bottom: 1rem;
}
h1 {
font-size: clamp(2.7rem, 5vw, 4.8rem);
margin: 0 0 1rem;
line-height: 1.05;
}
h2 {
font-size: clamp(2rem, 3vw, 3rem);
margin-bottom: 0.75rem;
}
h3 {
margin-bottom: 0.35rem;
}
.lead {
color: var(--muted);
font-size: 1.15rem;
margin-bottom: 1.5rem;
}
.hero__actions {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
margin-bottom: 1rem;
}
.btn {
border: 1px solid transparent;
padding: 0.85rem 1.4rem;
border-radius: 999px;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}
.btn.primary {
background: linear-gradient(90deg, var(--accent-strong), var(--accent));
color: #05060a;
border: none;
box-shadow: 0 15px 40px rgba(77, 213, 255, 0.3);
}
.btn.ghost {
border-color: rgba(255, 255, 255, 0.2);
color: var(--text);
background: transparent;
}
.btn:hover {
transform: translateY(-1px);
box-shadow: 0 12px 30px rgba(109, 132, 255, 0.25);
}
.install-command {
display: block;
padding: 1rem 1.25rem;
border-radius: 0.75rem;
background: rgba(18, 20, 34, 0.7);
border: 1px solid var(--card-border);
font-family: 'JetBrains Mono', 'Space Grotesk', monospace;
font-size: 0.95rem;
overflow-x: auto;
}
.hero__metrics {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
gap: 1rem;
}
.metric {
padding: 1.25rem;
border-radius: 1rem;
border: 1px solid var(--card-border);
background: rgba(5, 6, 10, 0.55);
}
.metric__label {
text-transform: uppercase;
font-size: 0.75rem;
letter-spacing: 0.25em;
color: var(--muted);
}
.metric__value {
font-size: 1.2rem;
margin: 0.35rem 0 0;
font-weight: 600;
}
.section {
padding: clamp(3rem, 8vw, 6rem) clamp(1.5rem, 5vw, 5rem);
}
.section__intro {
max-width: 42rem;
margin-bottom: 2.5rem;
}
.feature-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
gap: 1.25rem;
}
.card {
padding: 1.5rem;
border-radius: 1.2rem;
border: 1px solid var(--card-border);
background: var(--card);
box-shadow: var(--shadow);
min-height: 10rem;
}
.card p {
color: var(--muted);
margin: 0;
}
.install {
background: var(--bg-alt);
}
.install-steps {
list-style: none;
padding: 0;
margin: 0;
display: grid;
gap: 1.25rem;
}
.install-steps li {
padding: 1.5rem;
border-radius: 1rem;
border: 1px solid var(--card-border);
background: rgba(5, 6, 10, 0.5);
counter-increment: install-step;
position: relative;
padding-left: 4.5rem;
}
.install-steps li::before {
content: counter(install-step);
position: absolute;
left: 1.5rem;
top: 1.5rem;
width: 2rem;
height: 2rem;
border-radius: 50%;
background: rgba(77, 213, 255, 0.2);
border: 1px solid var(--accent);
display: grid;
place-items: center;
font-weight: 600;
}
.callout {
margin-top: 2rem;
padding: 1.25rem 1.5rem;
border-radius: 1rem;
background: rgba(255, 196, 87, 0.12);
border: 1px solid rgba(255, 196, 87, 0.35);
}
.showcase__grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
gap: 1.35rem;
}
figure {
margin: 0;
border: 1px solid var(--card-border);
border-radius: 1rem;
padding: 1rem;
background: rgba(9, 10, 18, 0.8);
min-height: 15rem;
}
figcaption {
font-weight: 600;
margin-bottom: 0.75rem;
color: var(--accent);
}
pre {
background: rgba(5, 6, 10, 0.65);
padding: 1rem;
border-radius: 0.75rem;
overflow: auto;
font-size: 0.85rem;
line-height: 1.3;
}
.statusbar-demo {
display: flex;
justify-content: space-between;
background: linear-gradient(90deg, var(--accent-strong), var(--accent));
color: #05060a;
padding: 0.65rem 1rem;
border-radius: 0.5rem;
font-weight: 600;
}
.community__actions {
display: flex;
gap: 0.75rem;
flex-wrap: wrap;
margin-top: 1rem;
}
.footer {
padding: 2rem clamp(1.5rem, 5vw, 5rem);
border-top: 1px solid rgba(255, 255, 255, 0.08);
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 1rem;
font-size: 0.9rem;
color: var(--muted);
}
@media (max-width: 720px) {
.nav {
flex-wrap: wrap;
}
.nav__toggle {
display: inline-flex;
}
.nav__links {
display: none;
width: 100%;
flex-direction: column;
padding: 1rem 0 0;
gap: 0.75rem;
}
.nav__links.is-open {
display: flex;
}
}