diff --git a/example_plugin.py b/example_plugin.py new file mode 100644 index 0000000..9276020 --- /dev/null +++ b/example_plugin.py @@ -0,0 +1,94 @@ +""" +Example ZDTT Plugin +This demonstrates how to create a plugin for ZDTT Terminal. + +To install this plugin: +1. Copy this file to ~/.zdtt/plugins/ +2. Restart ZDTT Terminal + +The plugin will be loaded automatically. +""" + +import subprocess +import os + + +def cmd_hello(args): + """Say hello to the user""" + if args: + name = ' '.join(args) + print(f"Hello, {name}! Welcome to ZDTT Terminal!") + else: + print("Hello! Welcome to ZDTT Terminal!") + + +def cmd_weather(args): + """Display weather information using wttr.in""" + location = args[0] if args else "" + try: + subprocess.run(['curl', f'wttr.in/{location}']) + except Exception as e: + print(f"Error fetching weather: {e}") + print("Make sure curl is installed: sudo apt-get install curl") + + +def cmd_sysinfo(args): + """Display detailed system information""" + print("\n=== System Information ===\n") + + # Hostname + print(f"Hostname: {os.uname().nodename}") + + # OS Info + try: + with open('/etc/os-release', 'r') as f: + for line in f: + if line.startswith('PRETTY_NAME'): + os_name = line.split('=')[1].strip().strip('"') + print(f"OS: {os_name}") + break + except: + pass + + # Kernel + print(f"Kernel: {os.uname().release}") + + # Architecture + print(f"Architecture: {os.uname().machine}") + + # CPU Info + try: + with open('/proc/cpuinfo', 'r') as f: + for line in f: + if 'model name' in line: + cpu = line.split(':')[1].strip() + print(f"CPU: {cpu}") + break + except: + pass + + # Memory Info + try: + with open('/proc/meminfo', 'r') as f: + for line in f: + if 'MemTotal' in line: + mem = int(line.split()[1]) // 1024 + print(f"Memory: {mem} MB") + break + except: + pass + + print() + + +def register_commands(): + """ + This function is required for ZDTT to load the plugin. + Return a dictionary of command names to functions. + """ + return { + 'hello': cmd_hello, + 'weather': cmd_weather, + 'sysinfo': cmd_sysinfo, + } + diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..51971a6 --- /dev/null +++ b/install.sh @@ -0,0 +1,220 @@ +#!/bin/bash +# +# ZDTT Terminal Installer +# Installs ZDTT Terminal and sets up the zdtt command +# + +set -e # Exit on error + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +echo "=========================================" +echo " ZDTT Terminal Installation Script" +echo "=========================================" +echo "" + +# Installation directories +INSTALL_DIR="$HOME/.local/share/zdtt" +BIN_DIR="$HOME/.local/bin" + +# Check if ZDTT is already installed +if [ -f "$BIN_DIR/zdtt" ] || [ -d "$INSTALL_DIR" ]; then + echo -e "${YELLOW}ZDTT Terminal is already installed!${NC}" + echo "" + echo "What would you like to do?" + echo " 1) Reinstall ZDTT Terminal" + echo " 2) Uninstall ZDTT Terminal" + echo " 3) Cancel" + echo "" + read -p "Enter your choice (1-3): " -n 1 -r + echo "" + echo "" + + case $REPLY in + 1) + echo "Reinstalling ZDTT Terminal..." + echo "" + # Don't remove the directory if we're running from it + # Just overwrite the files instead + ;; + 2) + echo "Uninstalling ZDTT Terminal..." + rm -rf "$INSTALL_DIR" + rm -f "$BIN_DIR/zdtt" + echo "" + echo -e "${GREEN}✓${NC} ZDTT Terminal has been uninstalled successfully." + echo "" + echo "Press any key to exit..." + read -n 1 -s -r + exit 0 + ;; + 3) + echo "Installation cancelled." + echo "" + echo "Press any key to exit..." + read -n 1 -s -r + exit 0 + ;; + *) + echo -e "${RED}Invalid choice.${NC} Installation cancelled." + echo "" + echo "Press any key to exit..." + read -n 1 -s -r + exit 1 + ;; + esac +fi + +# Check if running on Debian-based Linux +if [ ! -f /etc/debian_version ]; then + echo -e "${RED}Error: ZDTT Terminal only works on Debian-based Linux systems.${NC}" + echo "This does not appear to be a Debian-based distribution." + echo "" + echo "Press any key to exit..." + read -n 1 -s -r + exit 1 +fi + +echo -e "${GREEN}✓${NC} Debian-based Linux detected" + +# Check if Python 3 is installed +if ! command -v python3 &> /dev/null; then + echo -e "${RED}✗${NC} Python 3 is not installed" + echo "" + echo "Installing Python 3..." + + # Update package list and install Python 3 + sudo apt-get update + sudo apt-get install -y python3 + + if [ $? -ne 0 ]; then + echo -e "${RED}Failed to install Python 3${NC}" + echo "Please install Python 3 manually: sudo apt-get install python3" + echo "" + echo "Press any key to exit..." + read -n 1 -s -r + exit 1 + fi + + echo -e "${GREEN}✓${NC} Python 3 installed successfully" +else + PYTHON_VERSION=$(python3 --version 2>&1) + echo -e "${GREEN}✓${NC} Python 3 is already installed: ${PYTHON_VERSION}" +fi + +echo "" + +# Create directories if they don't exist +mkdir -p "$INSTALL_DIR" +mkdir -p "$BIN_DIR" + +echo "Installing ZDTT Terminal..." + +# Copy the terminal.py and install.sh to the installation directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cp "$SCRIPT_DIR/terminal.py" "$INSTALL_DIR/terminal.py" +cp "$SCRIPT_DIR/install.sh" "$INSTALL_DIR/install.sh" +chmod +x "$INSTALL_DIR/terminal.py" +chmod +x "$INSTALL_DIR/install.sh" + +echo -e "${GREEN}✓${NC} ZDTT Terminal files copied to $INSTALL_DIR" + +# Create the zdtt wrapper script +cat > "$BIN_DIR/zdtt" << 'EOF' +#!/bin/bash +# +# ZDTT Terminal Wrapper +# + +ZDTT_DIR="$HOME/.local/share/zdtt" + +case "$1" in + start) + # Clear screen before starting ZDTT + clear + python3 "$ZDTT_DIR/terminal.py" + ;; + installer|install|reinstall) + # Run the installer for reinstalling/updating + if [ -f "$ZDTT_DIR/install.sh" ]; then + bash "$ZDTT_DIR/install.sh" + else + echo "Error: Installer not found at $ZDTT_DIR/install.sh" + echo "Please download the installer from the ZDTT repository." + exit 1 + fi + ;; + uninstall) + echo "Uninstalling ZDTT Terminal..." + rm -rf "$ZDTT_DIR" + rm -f "$HOME/.local/bin/zdtt" + echo "ZDTT Terminal has been uninstalled." + ;; + version) + echo "ZDTT Terminal v0.0.1.alpha" + echo "" + echo "Features:" + echo " • Command history (↑/↓ navigation)" + echo " • Tab completion" + echo " • Colorized prompt" + echo " • Plugin system" + echo " • Native command support" + ;; + *) + echo "ZDTT Terminal" + echo "" + echo "Usage:" + echo " zdtt start - Start the ZDTT Terminal" + echo " zdtt installer - Run installer (for updates/reinstall)" + echo " zdtt version - Display version information" + echo " zdtt uninstall - Uninstall ZDTT Terminal" + echo "" + ;; +esac +EOF + +chmod +x "$BIN_DIR/zdtt" + +echo -e "${GREEN}✓${NC} ZDTT command installed to $BIN_DIR" + +# Check if ~/.local/bin is in PATH +if [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then + echo "" + echo -e "${YELLOW}Warning: $HOME/.local/bin is not in your PATH${NC}" + echo "" + echo "To use the 'zdtt' command, add the following line to your ~/.bashrc:" + echo "" + echo " export PATH=\"\$HOME/.local/bin:\$PATH\"" + echo "" + echo "Then run: source ~/.bashrc" + echo "" + + # Ask if user wants to add it automatically + read -p "Would you like to add it to your ~/.bashrc now? (y/n) " -n 1 -r + echo "" + if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "" >> "$HOME/.bashrc" + echo "# Added by ZDTT Terminal installer" >> "$HOME/.bashrc" + echo "export PATH=\"\$HOME/.local/bin:\$PATH\"" >> "$HOME/.bashrc" + echo -e "${GREEN}✓${NC} Added to ~/.bashrc" + echo "Please run: source ~/.bashrc" + fi +else + echo -e "${GREEN}✓${NC} ~/.local/bin is already in your PATH" +fi + +echo "" +echo "=========================================" +echo -e "${GREEN}Installation complete!${NC}" +echo "=========================================" +echo "" +echo "To start ZDTT Terminal, run:" +echo " zdtt start" +echo "" +echo "Press any key to exit..." +read -n 1 -s -r + diff --git a/terminal.py b/terminal.py new file mode 100644 index 0000000..f620626 --- /dev/null +++ b/terminal.py @@ -0,0 +1,598 @@ +#!/usr/bin/env python3 +""" +ZDTT Terminal - A custom terminal interface +Only works on Debian-based Linux systems +""" + +import os +import sys +import getpass +import subprocess +import shutil +import readline +import glob +import atexit + + +def check_debian_based(): + """Check if the system is Debian-based Linux""" + # Check if running on Linux + if sys.platform != 'linux': + print("Error: ZDTT Terminal only works on Debian-based Linux systems.") + print(f"Detected platform: {sys.platform}") + sys.exit(1) + + # Check for Debian-specific file + if not os.path.exists('/etc/debian_version'): + print("Error: ZDTT Terminal only works on Debian-based Linux systems.") + print("This does not appear to be a Debian-based distribution.") + print("(Debian, Ubuntu, Linux Mint, Pop!_OS, etc.)") + sys.exit(1) + + # Optionally, display the Debian version + try: + with open('/etc/debian_version', 'r') as f: + debian_version = f.read().strip() + # Silently note the version (for debugging if needed) + except: + pass + + +class ZDTTTerminal: + def __init__(self): + self.username = getpass.getuser() + self.running = True + self.current_dir = os.getcwd() + self.history_file = os.path.expanduser("~/.zdtt_history") + self.plugin_dir = os.path.expanduser("~/.zdtt/plugins") + + # ANSI color codes + self.COLOR_RESET = '\033[0m' + self.COLOR_GREEN = '\033[92m' + self.COLOR_BLUE = '\033[94m' + self.COLOR_CYAN = '\033[96m' + self.COLOR_BOLD = '\033[1m' + + self.commands = { + 'help': self.cmd_help, + 'clear': self.cmd_clear, + 'exit': self.cmd_exit, + 'quit': self.cmd_quit, + 'about': self.cmd_about, + 'echo': self.cmd_echo, + 'history': self.cmd_history, + 'plugins': self.cmd_plugins, + # System commands + 'ls': self.cmd_ls, + 'pwd': self.cmd_pwd, + 'cd': self.cmd_cd, + 'cat': self.cmd_cat, + 'nano': self.cmd_nano, + 'neofetch': self.cmd_neofetch, + 'mkdir': self.cmd_mkdir, + 'touch': self.cmd_touch, + 'rm': self.cmd_rm, + 'mv': self.cmd_mv, + 'cp': self.cmd_cp, + 'whoami': self.cmd_whoami, + 'date': self.cmd_date, + 'uname': self.cmd_uname, + 'grep': self.cmd_grep, + } + + # Setup readline history and tab completion + self.setup_readline() + + # Load plugins + self.load_plugins() + + def display_banner(self): + """Display the ZDTT ASCII art banner""" + banner = """ +░█████████ ░███████ ░██████████░██████████ + ░██ ░██ ░██ ░██ ░██ + ░██ ░██ ░██ ░██ ░██ + ░███ ░██ ░██ ░██ ░██ + ░██ ░██ ░██ ░██ ░██ + ░██ ░██ ░██ ░██ ░██ +░█████████ ░███████ ░██ ░██ + + +ZDTT Terminal v0.0.1.alpha +""" + print(banner) + + def setup_readline(self): + """Setup readline for history and tab completion""" + # Setup history + try: + readline.read_history_file(self.history_file) + except FileNotFoundError: + pass + + # Set history length + readline.set_history_length(1000) + + # Save history on exit + atexit.register(readline.write_history_file, self.history_file) + + # Setup tab completion + readline.set_completer(self.complete) + readline.parse_and_bind("tab: complete") + + # Enable arrow key navigation in history + readline.parse_and_bind("set editing-mode emacs") + + def complete(self, text, state): + """Tab completion function""" + # Get all possible completions + options = [] + + # Get the full line buffer + line = readline.get_line_buffer() + + # If we're at the start or completing a command + if line.startswith(text) or ' ' not in line[:readline.get_begidx()]: + # Complete command names + options = [cmd for cmd in self.commands.keys() if cmd.startswith(text)] + else: + # Complete filenames/directories + if text.startswith('~'): + text = os.path.expanduser(text) + + # Add glob pattern + if not text: + pattern = '*' + else: + pattern = text + '*' + + try: + matches = glob.glob(pattern) + options = matches + except: + options = [] + + # Return the state-th option + if state < len(options): + return options[state] + return None + + def load_plugins(self): + """Load plugin commands from the plugins directory""" + if not os.path.exists(self.plugin_dir): + os.makedirs(self.plugin_dir, exist_ok=True) + return + + # Look for Python files in the plugins directory + plugin_files = glob.glob(os.path.join(self.plugin_dir, "*.py")) + + for plugin_file in plugin_files: + try: + # Get plugin name + plugin_name = os.path.basename(plugin_file)[:-3] + + # Read and execute plugin file + with open(plugin_file, 'r') as f: + plugin_code = f.read() + + # Create a namespace for the plugin + plugin_namespace = {} + exec(plugin_code, plugin_namespace) + + # Look for register_command function + if 'register_commands' in plugin_namespace: + plugin_commands = plugin_namespace['register_commands']() + if isinstance(plugin_commands, dict): + self.commands.update(plugin_commands) + except Exception as e: + print(f"Warning: Failed to load plugin {plugin_name}: {e}") + + def get_prompt(self): + """Return the custom prompt string with colors""" + # Show current directory in prompt + cwd = os.getcwd() + # Show ~ for home directory + home = os.path.expanduser("~") + if cwd.startswith(home): + display_path = "~" + cwd[len(home):] + else: + display_path = cwd + + # Create colorized prompt + # [username in green @ ZDTT path in blue]=> + prompt = (f"[{self.COLOR_GREEN}{self.username}{self.COLOR_RESET}" + f"@{self.COLOR_CYAN}ZDTT{self.COLOR_RESET} " + f"{self.COLOR_BLUE}{display_path}{self.COLOR_RESET}]=> ") + return prompt + + def cmd_help(self, args): + """Display available commands""" + print("\nZDTT Terminal Commands:") + print(" help - Display this help message") + print(" clear - Clear the screen") + print(" echo - Echo a message") + print(" about - About ZDTT Terminal") + print(" history - Show command history") + print(" plugins - List loaded plugins") + print(" exit - Exit ZDTT (return to shell)") + print(" quit - Quit and close terminal window") + print() + print("File System Commands:") + print(" ls [options] - List directory contents") + print(" pwd - Print working directory") + print(" cd - Change directory") + print(" cat - Display file contents") + print(" mkdir - Create directory") + print(" touch - Create empty file") + print(" rm [-r] - Remove file or directory") + print(" mv - Move/rename file") + print(" cp [-r] - Copy file") + print(" grep - Search for pattern in file") + print() + print("System Commands:") + print(" whoami - Display current user") + print(" date - Display current date/time") + print(" uname [options] - Display system information") + print(" nano - Edit file with nano") + print(" neofetch - Display system info (auto-installs)") + print() + print("Features:") + print(" ↑/↓ arrows - Navigate command history") + print(" Tab - Auto-complete commands/files") + print(" -oszdtt flag - Run any command in system shell") + print(" Example: htop -oszdtt") + print() + + def cmd_clear(self, args): + """Clear the terminal screen""" + os.system('clear' if os.name != 'nt' else 'cls') + self.display_banner() + + def cmd_exit(self, args): + """Exit ZDTT Terminal (returns to parent shell)""" + print("Goodbye!") + self.running = False + + def cmd_quit(self, args): + """Quit and close the terminal window completely""" + print("Closing terminal window...") + # Exit the Python process with code 0 + # This will return control to the parent shell, which will then exit + sys.exit(0) + + def cmd_about(self, args): + """Display information about ZDTT Terminal""" + print("\nZDTT Terminal v0.0.1.alpha") + print("A custom terminal interface for Debian-based Linux") + print() + print("Features:") + print(" • Command history with ↑/↓ navigation") + print(" • Tab completion for commands and files") + print(" • Colorized prompt") + print(" • Plugin system for extensibility") + print(" • Native command support") + print(" • System command execution via -oszdtt flag") + print(" • Clean, premium interface") + print() + + def cmd_history(self, args): + """Display command history""" + history_length = readline.get_current_history_length() + + if history_length == 0: + print("No history available") + return + + # Show last 50 commands by default + limit = 50 + if args and args[0].isdigit(): + limit = int(args[0]) + + start = max(1, history_length - limit + 1) + + print() + for i in range(start, history_length + 1): + cmd = readline.get_history_item(i) + if cmd: + print(f"{i:4d} {cmd}") + print() + + def cmd_plugins(self, args): + """List loaded plugins""" + plugin_files = glob.glob(os.path.join(self.plugin_dir, "*.py")) + + if not plugin_files: + print("\nNo plugins installed.") + print(f"Plugin directory: {self.plugin_dir}") + print("\nTo create a plugin, create a .py file with a register_commands() function") + print("that returns a dictionary of command names to functions.") + print() + return + + print(f"\nLoaded Plugins ({len(plugin_files)}):") + for plugin_file in plugin_files: + plugin_name = os.path.basename(plugin_file)[:-3] + print(f" • {plugin_name}") + print() + + def cmd_echo(self, args): + """Echo the provided arguments""" + if args: + print(' '.join(args)) + else: + print() + + # File System Commands + + def cmd_ls(self, args): + """List directory contents""" + cmd = ['ls', '--color=auto'] + args + subprocess.run(cmd) + + def cmd_pwd(self, args): + """Print working directory""" + print(os.getcwd()) + + def cmd_cd(self, args): + """Change directory""" + if not args: + # Go to home directory + target = os.path.expanduser("~") + else: + target = args[0] + + try: + # Expand ~ and handle relative paths + target = os.path.expanduser(target) + os.chdir(target) + self.current_dir = os.getcwd() + except FileNotFoundError: + print(f"cd: {target}: No such file or directory") + except NotADirectoryError: + print(f"cd: {target}: Not a directory") + except PermissionError: + print(f"cd: {target}: Permission denied") + + def cmd_cat(self, args): + """Display file contents""" + if not args: + print("cat: missing file operand") + return + + for filename in args: + try: + with open(filename, 'r') as f: + print(f.read(), end='') + except FileNotFoundError: + print(f"cat: {filename}: No such file or directory") + except PermissionError: + print(f"cat: {filename}: Permission denied") + except IsADirectoryError: + print(f"cat: {filename}: Is a directory") + + def cmd_mkdir(self, args): + """Create directory""" + if not args: + print("mkdir: missing operand") + return + + for directory in args: + try: + os.makedirs(directory, exist_ok=False) + except FileExistsError: + print(f"mkdir: cannot create directory '{directory}': File exists") + except PermissionError: + print(f"mkdir: cannot create directory '{directory}': Permission denied") + + def cmd_touch(self, args): + """Create empty file""" + if not args: + print("touch: missing file operand") + return + + for filename in args: + try: + open(filename, 'a').close() + except PermissionError: + print(f"touch: cannot touch '{filename}': Permission denied") + + def cmd_rm(self, args): + """Remove file or directory""" + if not args: + print("rm: missing operand") + return + + # Separate flags from paths + flags = [arg for arg in args if arg.startswith('-')] + paths = [arg for arg in args if not arg.startswith('-')] + + if not paths: + print("rm: missing operand") + return + + # Check for recursive flag + recursive = '-r' in flags or '-rf' in flags or '-fr' in flags + force = '-f' in flags or '-rf' in flags or '-fr' in flags + + for path in paths: + try: + if os.path.isfile(path): + os.remove(path) + elif os.path.isdir(path): + if recursive: + shutil.rmtree(path) + else: + print(f"rm: cannot remove '{path}': Is a directory") + print("rm: use 'rm -r' to remove directories") + else: + if not force: + print(f"rm: cannot remove '{path}': No such file or directory") + except PermissionError: + if not force: + print(f"rm: cannot remove '{path}': Permission denied") + except Exception as e: + if not force: + print(f"rm: error removing '{path}': {e}") + + def cmd_mv(self, args): + """Move/rename file""" + if len(args) < 2: + print("mv: missing file operand") + return + + src = args[0] + dest = args[1] + + try: + shutil.move(src, dest) + except FileNotFoundError: + print(f"mv: cannot stat '{src}': No such file or directory") + except PermissionError: + print(f"mv: cannot move '{src}': Permission denied") + + def cmd_cp(self, args): + """Copy file""" + if len(args) < 2: + print("cp: missing file operand") + return + + # Separate flags from paths + flags = [arg for arg in args if arg.startswith('-')] + paths = [arg for arg in args if not arg.startswith('-')] + + if len(paths) < 2: + print("cp: missing destination file operand") + return + + src = paths[0] + dest = paths[1] + + # Check for recursive flag + recursive = '-r' in flags or '-R' in flags + + try: + if os.path.isfile(src): + shutil.copy2(src, dest) + elif os.path.isdir(src): + if recursive: + shutil.copytree(src, dest) + else: + print(f"cp: -r not specified; omitting directory '{src}'") + else: + print(f"cp: cannot stat '{src}': No such file or directory") + except FileNotFoundError: + print(f"cp: cannot stat '{src}': No such file or directory") + except PermissionError: + print(f"cp: cannot create '{dest}': Permission denied") + except FileExistsError: + print(f"cp: cannot create directory '{dest}': File exists") + + def cmd_grep(self, args): + """Search for pattern in file""" + if len(args) < 2: + print("grep: missing pattern or file") + return + + cmd = ['grep', '--color=auto'] + args + subprocess.run(cmd) + + # System Commands + + def cmd_whoami(self, args): + """Display current user""" + print(self.username) + + def cmd_date(self, args): + """Display current date/time""" + subprocess.run(['date'] + args) + + def cmd_uname(self, args): + """Display system information""" + subprocess.run(['uname'] + args) + + def cmd_nano(self, args): + """Edit file with nano""" + if not args: + print("nano: missing file operand") + return + + subprocess.run(['nano'] + args) + + def cmd_neofetch(self, args): + """Display system info with neofetch (auto-installs if needed)""" + # Check if neofetch is installed + if not shutil.which('neofetch'): + print("neofetch is not installed. Installing...") + print() + try: + subprocess.run(['sudo', 'apt-get', 'install', '-y', 'neofetch'], check=True) + print() + print("neofetch installed successfully!") + print() + except subprocess.CalledProcessError: + print("Failed to install neofetch") + return + + subprocess.run(['neofetch'] + args) + + def execute_command(self, command_line): + """Parse and execute a command""" + if not command_line.strip(): + return + + # Check for -oszdtt flag (Outside ZDTT) + if '-oszdtt' in command_line: + # Remove the -oszdtt flag and execute as system command + system_command = command_line.replace('-oszdtt', '').strip() + if system_command: + try: + result = os.system(system_command) + # os.system returns the exit code + if result != 0: + pass # Command already displayed its error + except Exception as e: + print(f"Error executing command: {e}") + else: + print("No command specified with -oszdtt flag") + return + + parts = command_line.strip().split() + cmd = parts[0].lower() + args = parts[1:] if len(parts) > 1 else [] + + if cmd in self.commands: + self.commands[cmd](args) + else: + print(f"Command not found: {cmd}") + print("Type 'help' for available commands.") + print("Tip: Use -oszdtt flag to run system commands") + + def run(self): + """Main terminal loop""" + # Clear screen and display banner + os.system('clear' if os.name != 'nt' else 'cls') + self.display_banner() + + # Main command loop + while self.running: + try: + command = input(self.get_prompt()) + self.execute_command(command) + except KeyboardInterrupt: + print("\nUse 'exit' to return to shell, or 'quit' to close the window.") + except EOFError: + print("\nGoodbye!") + break + + +def main(): + # Check if running on Debian-based Linux + check_debian_based() + + terminal = ZDTTTerminal() + terminal.run() + + +if __name__ == "__main__": + main() +