Update version to v0.1.2.a.3 and enhance UI/UX features

- Updated version number in version.txt.
- Improved smooth scrolling for anchor links in script.js.
- Added Intersection Observer for fade-in animations in script.js.
- Enhanced CSS styles for various elements, including buttons, metrics, and backgrounds for better visual appeal and interactivity.
This commit is contained in:
2025-11-15 13:52:40 -05:00
parent 193a434b32
commit dca47ab1eb
5 changed files with 695 additions and 153 deletions

View File

@@ -45,7 +45,7 @@
<div class="hero__metrics"> <div class="hero__metrics">
<div class="metric"> <div class="metric">
<p class="metric__label">Current release</p> <p class="metric__label">Current release</p>
<p class="metric__value">v0.1.2.a.2</p> <p class="metric__value">v0.1.2.a.3</p>
</div> </div>
<div class="metric"> <div class="metric">
<p class="metric__label">Supported families</p> <p class="metric__label">Supported families</p>

View File

@@ -72,4 +72,46 @@ document.addEventListener('DOMContentLoaded', () => {
} }
}); });
}); });
// Smooth scroll for anchor links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
const href = this.getAttribute('href');
if (href === '#' || href === '#top') return;
e.preventDefault();
const target = document.querySelector(href);
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
// Intersection Observer for fade-in animations
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
// Observe sections and cards for fade-in effect (exclude hero section)
const animatedElements = document.querySelectorAll('.section:not(.hero), .card, .metric, figure, .install-steps li');
animatedElements.forEach(el => {
el.style.opacity = '0';
el.style.transform = 'translateY(20px)';
el.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
observer.observe(el);
});
}); });

View File

@@ -4,12 +4,14 @@
--bg: #05060a; --bg: #05060a;
--bg-alt: #0e1018; --bg-alt: #0e1018;
--card: #121422; --card: #121422;
--card-border: rgba(255, 255, 255, 0.08); --card-border: rgba(255, 255, 255, 0.12);
--text: #f5f6fd; --text: #f5f6fd;
--muted: #9aa2c4; --muted: #9aa2c4;
--accent: #4dd5ff; --accent: #4dd5ff;
--accent-strong: #6d84ff; --accent-strong: #6d84ff;
--accent-glow: rgba(77, 213, 255, 0.4);
--shadow: 0 18px 45px rgba(4, 6, 11, 0.6); --shadow: 0 18px 45px rgba(4, 6, 11, 0.6);
--shadow-glow: 0 0 30px rgba(77, 213, 255, 0.2);
} }
*, *,
@@ -18,15 +20,43 @@
box-sizing: border-box; box-sizing: border-box;
} }
html {
scroll-behavior: smooth;
}
body { body {
margin: 0; margin: 0;
min-height: 100vh; min-height: 100vh;
background: radial-gradient(circle at top, rgba(77, 213, 255, 0.12), transparent 55%), background:
radial-gradient(circle at 20% 20%, rgba(109, 132, 255, 0.25), transparent 35%), radial-gradient(circle at 0% 0%, rgba(77, 213, 255, 0.15), transparent 50%),
var(--bg); radial-gradient(circle at 100% 0%, rgba(109, 132, 255, 0.2), transparent 50%),
radial-gradient(circle at 50% 100%, rgba(77, 213, 255, 0.08), transparent 60%),
var(--bg);
background-attachment: fixed;
color: var(--text); color: var(--text);
font-size: 1rem; font-size: 1rem;
line-height: 1.6; line-height: 1.6;
position: relative;
overflow-x: hidden;
}
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
radial-gradient(circle at 20% 30%, rgba(77, 213, 255, 0.03), transparent 50%),
radial-gradient(circle at 80% 70%, rgba(109, 132, 255, 0.03), transparent 50%);
pointer-events: none;
z-index: 0;
}
body > * {
position: relative;
z-index: 1;
} }
img { img {
@@ -56,6 +86,27 @@ button:focus-visible {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 2rem; gap: 2rem;
position: relative;
overflow: hidden;
}
.hero::after {
content: '';
position: absolute;
top: -50%;
right: -20%;
width: 600px;
height: 600px;
background: radial-gradient(circle, rgba(77, 213, 255, 0.1), transparent 70%);
border-radius: 50%;
filter: blur(60px);
pointer-events: none;
animation: pulse 8s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1) translate(0, 0); opacity: 0.6; }
50% { transform: scale(1.2) translate(-10%, 10%); opacity: 0.8; }
} }
.nav { .nav {
@@ -68,6 +119,12 @@ button:focus-visible {
.brand { .brand {
font-weight: 700; font-weight: 700;
letter-spacing: 0.08em; letter-spacing: 0.08em;
font-size: 1.25rem;
background: linear-gradient(135deg, var(--accent), var(--accent-strong));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: 0 0 20px var(--accent-glow);
} }
.nav__links { .nav__links {
@@ -79,14 +136,32 @@ button:focus-visible {
.nav__links a { .nav__links a {
color: var(--muted); color: var(--muted);
font-weight: 500; font-weight: 500;
transition: color 0.2s ease; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
text-decoration: none; text-decoration: none;
position: relative;
padding: 0.5rem 0;
}
.nav__links a::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 0;
height: 2px;
background: linear-gradient(90deg, var(--accent), var(--accent-strong));
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 0 10px var(--accent-glow);
} }
.nav__links a:hover { .nav__links a:hover {
color: var(--text); color: var(--text);
} }
.nav__links a:hover::after {
width: 100%;
}
.nav__toggle { .nav__toggle {
display: none; display: none;
background: none; background: none;
@@ -119,6 +194,11 @@ h1 {
font-size: clamp(2.7rem, 5vw, 4.8rem); font-size: clamp(2.7rem, 5vw, 4.8rem);
margin: 0 0 1rem; margin: 0 0 1rem;
line-height: 1.05; line-height: 1.05;
background: linear-gradient(135deg, var(--text) 0%, var(--accent) 50%, var(--accent-strong) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
filter: drop-shadow(0 0 20px rgba(77, 213, 255, 0.3));
} }
h2 { h2 {
@@ -149,8 +229,28 @@ h3 {
border-radius: 999px; border-radius: 999px;
font-weight: 600; font-weight: 600;
cursor: pointer; cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
text-decoration: none; text-decoration: none;
position: relative;
overflow: hidden;
}
.btn::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
transform: translate(-50%, -50%);
transition: width 0.6s, height 0.6s;
}
.btn:hover::before {
width: 300px;
height: 300px;
} }
.btn:focus-visible { .btn:focus-visible {
@@ -163,32 +263,51 @@ h3 {
} }
.btn.primary { .btn.primary {
background: linear-gradient(90deg, var(--accent-strong), var(--accent)); background: linear-gradient(135deg, var(--accent-strong), var(--accent));
color: #05060a; color: #05060a;
border: none; border: none;
box-shadow: 0 15px 40px rgba(77, 213, 255, 0.3); box-shadow: 0 15px 40px rgba(77, 213, 255, 0.3), 0 0 20px rgba(77, 213, 255, 0.2);
z-index: 1;
}
.btn.primary:hover {
transform: translateY(-2px) scale(1.02);
box-shadow: 0 20px 50px rgba(77, 213, 255, 0.4), 0 0 30px rgba(77, 213, 255, 0.3);
} }
.btn.ghost { .btn.ghost {
border-color: rgba(255, 255, 255, 0.2); border-color: rgba(255, 255, 255, 0.25);
color: var(--text); color: var(--text);
background: transparent; background: rgba(255, 255, 255, 0.03);
backdrop-filter: blur(10px);
z-index: 1;
} }
.btn:hover { .btn.ghost:hover {
transform: translateY(-1px); transform: translateY(-2px);
box-shadow: 0 12px 30px rgba(109, 132, 255, 0.25); border-color: rgba(77, 213, 255, 0.5);
background: rgba(77, 213, 255, 0.1);
box-shadow: 0 10px 30px rgba(77, 213, 255, 0.15);
} }
.install-command { .install-command {
display: block; display: block;
padding: 1rem 1.25rem; padding: 1rem 1.25rem;
border-radius: 0.75rem; border-radius: 0.75rem;
background: rgba(18, 20, 34, 0.7); background: rgba(18, 20, 34, 0.8);
border: 1px solid var(--card-border); border: 1px solid var(--card-border);
font-family: 'JetBrains Mono', 'Space Grotesk', monospace; font-family: 'JetBrains Mono', 'Space Grotesk', monospace;
font-size: 0.95rem; font-size: 0.95rem;
overflow-x: auto; overflow-x: auto;
backdrop-filter: blur(10px);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
position: relative;
}
.install-command:hover {
border-color: rgba(77, 213, 255, 0.3);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3), 0 0 20px rgba(77, 213, 255, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.1);
} }
.hero__metrics { .hero__metrics {
@@ -201,7 +320,32 @@ h3 {
padding: 1.25rem; padding: 1.25rem;
border-radius: 1rem; border-radius: 1rem;
border: 1px solid var(--card-border); border: 1px solid var(--card-border);
background: rgba(5, 6, 10, 0.55); background: rgba(5, 6, 10, 0.6);
backdrop-filter: blur(10px);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.metric::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(77, 213, 255, 0.1), transparent);
transition: left 0.5s ease;
}
.metric:hover {
transform: translateY(-4px);
border-color: rgba(77, 213, 255, 0.3);
box-shadow: 0 10px 30px rgba(77, 213, 255, 0.15);
}
.metric:hover::before {
left: 100%;
} }
.metric__label { .metric__label {
@@ -239,6 +383,40 @@ h3 {
background: var(--card); background: var(--card);
box-shadow: var(--shadow); box-shadow: var(--shadow);
min-height: 10rem; min-height: 10rem;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
backdrop-filter: blur(10px);
}
.card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, transparent, var(--accent), transparent);
transform: translateX(-100%);
transition: transform 0.6s ease;
}
.card:hover {
transform: translateY(-6px);
border-color: rgba(77, 213, 255, 0.4);
box-shadow: 0 20px 50px rgba(77, 213, 255, 0.2), var(--shadow);
}
.card:hover::before {
transform: translateX(100%);
}
.card h3 {
transition: color 0.3s ease;
}
.card:hover h3 {
color: var(--accent);
} }
.card p { .card p {
@@ -262,10 +440,19 @@ h3 {
padding: 1.5rem; padding: 1.5rem;
border-radius: 1rem; border-radius: 1rem;
border: 1px solid var(--card-border); border: 1px solid var(--card-border);
background: rgba(5, 6, 10, 0.5); background: rgba(5, 6, 10, 0.6);
backdrop-filter: blur(10px);
counter-increment: install-step; counter-increment: install-step;
position: relative; position: relative;
padding-left: 4.5rem; padding-left: 4.5rem;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.install-steps li:hover {
transform: translateX(8px);
border-color: rgba(77, 213, 255, 0.3);
background: rgba(5, 6, 10, 0.7);
box-shadow: 0 10px 30px rgba(77, 213, 255, 0.1);
} }
.install-steps li::before { .install-steps li::before {
@@ -281,6 +468,14 @@ h3 {
display: grid; display: grid;
place-items: center; place-items: center;
font-weight: 600; font-weight: 600;
transition: all 0.3s ease;
box-shadow: 0 0 15px rgba(77, 213, 255, 0.3);
}
.install-steps li:hover::before {
background: rgba(77, 213, 255, 0.3);
box-shadow: 0 0 25px rgba(77, 213, 255, 0.5);
transform: scale(1.1);
} }
.callout { .callout {
@@ -289,6 +484,14 @@ h3 {
border-radius: 1rem; border-radius: 1rem;
background: rgba(255, 196, 87, 0.12); background: rgba(255, 196, 87, 0.12);
border: 1px solid rgba(255, 196, 87, 0.35); border: 1px solid rgba(255, 196, 87, 0.35);
backdrop-filter: blur(10px);
box-shadow: 0 8px 32px rgba(255, 196, 87, 0.1);
transition: all 0.3s ease;
}
.callout:hover {
border-color: rgba(255, 196, 87, 0.5);
box-shadow: 0 8px 32px rgba(255, 196, 87, 0.2);
} }
.showcase__grid { .showcase__grid {
@@ -303,7 +506,33 @@ figure {
border-radius: 1rem; border-radius: 1rem;
padding: 1rem; padding: 1rem;
background: rgba(9, 10, 18, 0.8); background: rgba(9, 10, 18, 0.8);
backdrop-filter: blur(10px);
min-height: 15rem; min-height: 15rem;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
figure::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(77, 213, 255, 0.1), transparent 70%);
opacity: 0;
transition: opacity 0.4s ease;
}
figure:hover {
transform: translateY(-8px) scale(1.02);
border-color: rgba(77, 213, 255, 0.4);
box-shadow: 0 20px 50px rgba(77, 213, 255, 0.2);
}
figure:hover::before {
opacity: 1;
} }
figcaption { figcaption {
@@ -313,22 +542,55 @@ figcaption {
} }
pre { pre {
background: rgba(5, 6, 10, 0.65); background: rgba(5, 6, 10, 0.8);
padding: 1rem; padding: 1rem;
border-radius: 0.75rem; border-radius: 0.75rem;
overflow: auto; overflow: auto;
font-size: 0.85rem; font-size: 0.85rem;
line-height: 1.3; line-height: 1.3;
border: 1px solid rgba(77, 213, 255, 0.1);
box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.3);
position: relative;
backdrop-filter: blur(5px);
}
pre::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(77, 213, 255, 0.3), transparent);
} }
.statusbar-demo { .statusbar-demo {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
background: linear-gradient(90deg, var(--accent-strong), var(--accent)); background: linear-gradient(135deg, var(--accent-strong), var(--accent));
color: #05060a; color: #05060a;
padding: 0.65rem 1rem; padding: 0.65rem 1rem;
border-radius: 0.5rem; border-radius: 0.5rem;
font-weight: 600; font-weight: 600;
box-shadow: 0 4px 20px rgba(77, 213, 255, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.2);
position: relative;
overflow: hidden;
}
.statusbar-demo::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
animation: shimmer 3s infinite;
}
@keyframes shimmer {
0% { left: -100%; }
100% { left: 100%; }
} }
.community__actions { .community__actions {
@@ -340,7 +602,7 @@ pre {
.footer { .footer {
padding: 2rem clamp(1.5rem, 5vw, 5rem); padding: 2rem clamp(1.5rem, 5vw, 5rem);
border-top: 1px solid rgba(255, 255, 255, 0.08); border-top: 1px solid rgba(255, 255, 255, 0.1);
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
@@ -348,6 +610,28 @@ pre {
gap: 1rem; gap: 1rem;
font-size: 0.9rem; font-size: 0.9rem;
color: var(--muted); color: var(--muted);
background: rgba(5, 6, 10, 0.5);
backdrop-filter: blur(10px);
position: relative;
}
.footer::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(77, 213, 255, 0.3), transparent);
}
.footer a {
transition: all 0.3s ease;
}
.footer a:hover {
color: var(--accent);
text-shadow: 0 0 10px var(--accent-glow);
} }
@media (max-width: 720px) { @media (max-width: 720px) {
@@ -365,9 +649,27 @@ pre {
flex-direction: column; flex-direction: column;
padding: 1rem 0 0; padding: 1rem 0 0;
gap: 0.75rem; gap: 0.75rem;
background: rgba(5, 6, 10, 0.95);
backdrop-filter: blur(20px);
border-radius: 0.5rem;
padding: 1rem;
margin-top: 1rem;
border: 1px solid var(--card-border);
} }
.nav__links.is-open { .nav__links.is-open {
display: flex; display: flex;
animation: slideDown 0.3s ease;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
} }
} }

View File

@@ -16,6 +16,7 @@ import logging
import threading import threading
import json import json
import shlex import shlex
import signal
from datetime import datetime from datetime import datetime
import urllib.request import urllib.request
import urllib.error import urllib.error
@@ -218,6 +219,7 @@ class ZDTTTerminal:
self.scroll_region_set = False self.scroll_region_set = False
self.plugin_command_names = set() self.plugin_command_names = set()
self.update_check_thread = None self.update_check_thread = None
self.resize_lock = threading.Lock() # Lock for resize operations
# Setup logging for plugins # Setup logging for plugins
self.setup_logging() self.setup_logging()
@@ -232,12 +234,50 @@ class ZDTTTerminal:
# Load user preferences (status bar color, etc.) # Load user preferences (status bar color, etc.)
self.load_preferences() self.load_preferences()
# ANSI color codes # ANSI color codes - Enhanced palette
self.COLOR_RESET = '\033[0m' 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.COLOR_BOLD = '\033[1m'
self.COLOR_DIM = '\033[2m'
self.COLOR_ITALIC = '\033[3m'
# Standard colors
self.COLOR_BLACK = '\033[30m'
self.COLOR_RED = '\033[31m'
self.COLOR_GREEN = '\033[32m'
self.COLOR_YELLOW = '\033[33m'
self.COLOR_BLUE = '\033[34m'
self.COLOR_MAGENTA = '\033[35m'
self.COLOR_CYAN = '\033[36m'
self.COLOR_WHITE = '\033[37m'
# Bright colors
self.COLOR_BRIGHT_BLACK = '\033[90m'
self.COLOR_BRIGHT_RED = '\033[91m'
self.COLOR_BRIGHT_GREEN = '\033[92m'
self.COLOR_BRIGHT_YELLOW = '\033[93m'
self.COLOR_BRIGHT_BLUE = '\033[94m'
self.COLOR_BRIGHT_MAGENTA = '\033[95m'
self.COLOR_BRIGHT_CYAN = '\033[96m'
self.COLOR_BRIGHT_WHITE = '\033[97m'
# Accent colors (using bright variants for better visibility)
self.COLOR_ACCENT = '\033[96m' # Bright cyan
self.COLOR_ACCENT2 = '\033[94m' # Bright blue
self.COLOR_SUCCESS = '\033[92m' # Bright green
self.COLOR_WARNING = '\033[93m' # Bright yellow
self.COLOR_ERROR = '\033[91m' # Bright red
self.COLOR_INFO = '\033[96m' # Bright cyan
# Background colors
self.BG_BLACK = '\033[40m'
self.BG_RED = '\033[41m'
self.BG_GREEN = '\033[42m'
self.BG_YELLOW = '\033[43m'
self.BG_BLUE = '\033[44m'
self.BG_MAGENTA = '\033[45m'
self.BG_CYAN = '\033[46m'
self.BG_WHITE = '\033[47m'
self.BG_BRIGHT_CYAN = '\033[106m'
self.commands = { self.commands = {
'help': self.cmd_help, 'help': self.cmd_help,
@@ -473,35 +513,106 @@ ZDTT Terminal v{self.version}
def _render_status_bar(self): def _render_status_bar(self):
"""Render a single-line status bar with branding and time.""" """Render a single-line status bar with branding and time."""
bar_text = self._build_status_bar_text()
try: try:
# Get terminal size first to ensure we don't write beyond bounds
try:
term_size = shutil.get_terminal_size()
max_width = term_size.columns
except Exception:
max_width = 80 # Fallback
bar_text = self._build_status_bar_text()
# Ensure bar_text doesn't exceed terminal width (safety check)
# Count visible characters (approximate - ANSI codes don't count)
# This is a rough check, but better than nothing
if len(bar_text) > max_width * 3: # Allow for ANSI codes (rough estimate)
# Rebuild with safer width
bar_text = self._build_status_bar_text()
sys.stdout.write("\033[s") # Save cursor position sys.stdout.write("\033[s") # Save cursor position
sys.stdout.write("\033[1;1H") # Move to first row sys.stdout.write("\033[1;1H") # Move to first row, first column
sys.stdout.write("\033[2K") # Clear the line sys.stdout.write("\033[2K") # Clear the entire line
sys.stdout.write("\033[0m") # Reset all attributes
sys.stdout.write(bar_text) sys.stdout.write(bar_text)
sys.stdout.write(self.COLOR_RESET) sys.stdout.write("\033[0m") # Ensure reset at end
# Move cursor to end of line to prevent wrapping issues
sys.stdout.write(f"\033[{max_width}G") # Move to column max_width
sys.stdout.write("\033[u") # Restore cursor sys.stdout.write("\033[u") # Restore cursor
sys.stdout.flush() sys.stdout.flush()
except Exception: except Exception:
print(bar_text) # Fallback: just skip rendering if there's an error
pass
def _build_status_bar_text(self): def _build_status_bar_text(self):
left_text = "ZDTT by ZaneDev" """Render a single-line status bar with enhanced branding and time."""
time_str = datetime.now().strftime("%I:%M:%p").lower() left_text = f"{self.COLOR_BOLD}ZDTT{self.COLOR_RESET} by {self.COLOR_BOLD}ZaneDev{self.COLOR_RESET}"
try: time_str = datetime.now().strftime("%I:%M %p")
term_size = shutil.get_terminal_size() plain_left = "ZDTT by ZaneDev"
width = max(term_size.columns, len(left_text) + len(time_str) + 4) plain_time = time_str
except Exception:
width = len(left_text) + len(time_str) + 4
padding = max(width - len(left_text) - len(time_str) - 2, 1) try:
bar_plain = f" {left_text}{' ' * padding}{time_str} " # Always get fresh terminal size to handle resizes
if len(bar_plain) < width: term_size = shutil.get_terminal_size()
bar_plain = bar_plain.ljust(width) width = term_size.columns
# Safety: ensure width is at least 1
width = max(1, width)
except Exception:
# Fallback to minimum width if we can't get terminal size
width = max(1, len(plain_left) + len(plain_time) + 6)
# Calculate the minimum content width (plain text only, no ANSI codes)
# Format: " ZDTT by ZaneDev | TIME "
min_content_width = len(plain_left) + len(plain_time) + 5 # 5 = spaces + separator
# Calculate padding to fill the line
if width < min_content_width:
# Terminal too narrow, use minimum padding
padding = 0
else: else:
bar_plain = bar_plain[:width] padding = width - min_content_width
# Build the content (plain text calculation)
separator = f"{self.COLOR_DIM}{self.COLOR_RESET}"
bar_content = f" {left_text} {' ' * padding}{separator} {self.COLOR_BRIGHT_WHITE}{time_str}{self.COLOR_RESET} "
# Calculate actual display length (plain text only)
actual_display_len = len(plain_left) + len(plain_time) + padding + 5
# Ensure we fill exactly to terminal width (but never exceed it)
if actual_display_len < width:
# Add trailing spaces to fill exactly to width
trailing_spaces = width - actual_display_len
bar_content = bar_content.rstrip() + ' ' * trailing_spaces
elif actual_display_len > width:
# We exceeded width, recalculate with less padding
padding = max(0, width - min_content_width)
bar_content = f" {left_text} {' ' * padding}{separator} {self.COLOR_BRIGHT_WHITE}{time_str}{self.COLOR_RESET} "
actual_display_len = len(plain_left) + len(plain_time) + padding + 5
if actual_display_len < width:
trailing_spaces = width - actual_display_len
bar_content = bar_content.rstrip() + ' ' * trailing_spaces
else:
# Still too wide, trim the time if necessary
if width < len(plain_left) + 10:
# Very narrow terminal, just show minimal content
bar_content = f" {left_text} {separator} {self.COLOR_BRIGHT_WHITE}{time_str[:8]}{self.COLOR_RESET} "
bar_content = bar_content[:width] if len(bar_content) > width else bar_content
# Final safety check: ensure we never exceed terminal width
# This is approximate since ANSI codes don't count, but better than nothing
bg_code, fg_code = STATUS_BAR_COLORS.get(self.status_bar_color, ('44', '97')) 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}" result = f"\033[{bg_code}m\033[{fg_code}m{bar_content}\033[0m"
# If the result is suspiciously long, truncate it
# (rough heuristic: ANSI codes add ~30-50 chars, so if result > width*2, it's probably wrong)
if len(result) > width * 2:
# Emergency fallback: simple status bar
simple_bar = f" ZDTT by ZaneDev | {time_str} "
simple_bar = simple_bar[:width] if len(simple_bar) > width else simple_bar.ljust(width)
result = f"\033[{bg_code}m\033[{fg_code}m{simple_bar}\033[0m"
return result
def _set_scroll_region(self): def _set_scroll_region(self):
"""Reserve the top row for the status bar.""" """Reserve the top row for the status bar."""
@@ -525,6 +636,50 @@ ZDTT Terminal v{self.version}
sys.stdout.flush() sys.stdout.flush()
self.scroll_region_set = False self.scroll_region_set = False
def _handle_resize(self, signum=None, frame=None):
"""Handle terminal resize event (SIGWINCH)."""
# Use a lock to prevent race conditions
if not self.resize_lock.acquire(blocking=False):
# If we can't acquire the lock immediately, skip this resize
# (another resize is already being handled)
return
try:
# Small delay to let terminal settle after resize
time_module.sleep(0.05)
# Reset scroll region first to clear any corrupted state
self._reset_scroll_region()
# Update scroll region with new terminal size
self._set_scroll_region()
# Clear the status bar line completely before redrawing
try:
sys.stdout.write("\033[1;1H") # Move to first row
sys.stdout.write("\033[2K") # Clear the entire line
sys.stdout.write("\033[0m") # Reset attributes
sys.stdout.flush()
except Exception:
pass
# Force immediate status bar refresh
self._render_status_bar()
# Ensure cursor is in a safe position
try:
term_size = shutil.get_terminal_size()
sys.stdout.write(f"\033[{term_size.lines};1H") # Move to last line, first column
sys.stdout.flush()
except Exception:
pass
except Exception:
# Silently fail if resize handling fails
pass
finally:
self.resize_lock.release()
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
@@ -626,7 +781,7 @@ ZDTT Terminal v{self.version}
# Show brief status if there were failures # Show brief status if there were failures
if failed_count > 0: if failed_count > 0:
print(f"{failed_count} plugin(s) failed to load. Check ~/.zdtt/plugin_errors.log") print(f"{self.COLOR_WARNING}{failed_count} plugin(s) failed to load. Check ~/.zdtt/plugin_errors.log{self.COLOR_RESET}")
def unload_plugin_commands(self): def unload_plugin_commands(self):
"""Remove commands that originated from plugins.""" """Remove commands that originated from plugins."""
@@ -668,7 +823,7 @@ ZDTT Terminal v{self.version}
f.write(f"{name}={command}\n") f.write(f"{name}={command}\n")
except Exception as e: except Exception as e:
logging.error(f"Failed to save aliases: {e}") logging.error(f"Failed to save aliases: {e}")
print(f"Error: Failed to save aliases: {e}") print(f"{self.COLOR_ERROR}Error: Failed to save aliases: {e}{self.COLOR_RESET}")
def expand_aliases(self, command_line): def expand_aliases(self, command_line):
"""Expand aliases in command line""" """Expand aliases in command line"""
@@ -689,7 +844,7 @@ ZDTT Terminal v{self.version}
return command_line return command_line
def get_prompt(self): def get_prompt(self):
"""Return the custom prompt string with colors""" """Return the custom prompt string with enhanced colors"""
# Show current directory in prompt # Show current directory in prompt
cwd = os.getcwd() cwd = os.getcwd()
# Show ~ for home directory # Show ~ for home directory
@@ -704,63 +859,72 @@ ZDTT Terminal v{self.version}
RL_PROMPT_START = '\001' RL_PROMPT_START = '\001'
RL_PROMPT_END = '\002' RL_PROMPT_END = '\002'
# Create colorized prompt with readline-safe escape codes # Create enhanced colorized prompt with gradient-like effect
# [username in green @ ZDTT path in blue]=> # [username in bright green @ ZDTT in bright cyan path in bright blue]=>
prompt = (f"[{RL_PROMPT_START}{self.COLOR_GREEN}{RL_PROMPT_END}{self.username}" prompt = (f"{RL_PROMPT_START}{self.COLOR_BRIGHT_CYAN}{RL_PROMPT_END}┌─{RL_PROMPT_START}{self.COLOR_RESET}{RL_PROMPT_END}"
f"[{RL_PROMPT_START}{self.COLOR_BRIGHT_GREEN}{RL_PROMPT_END}{self.username}"
f"{RL_PROMPT_START}{self.COLOR_RESET}{RL_PROMPT_END}" f"{RL_PROMPT_START}{self.COLOR_RESET}{RL_PROMPT_END}"
f"@{RL_PROMPT_START}{self.COLOR_CYAN}{RL_PROMPT_END}ZDTT{RL_PROMPT_START}{self.COLOR_RESET}{RL_PROMPT_END} " f"{RL_PROMPT_START}{self.COLOR_BRIGHT_WHITE}{RL_PROMPT_END}@{RL_PROMPT_START}{self.COLOR_RESET}{RL_PROMPT_END}"
f"{RL_PROMPT_START}{self.COLOR_BLUE}{RL_PROMPT_END}{display_path}" f"{RL_PROMPT_START}{self.COLOR_BRIGHT_CYAN}{RL_PROMPT_END}ZDTT{RL_PROMPT_START}{self.COLOR_RESET}{RL_PROMPT_END} "
f"{RL_PROMPT_START}{self.COLOR_RESET}{RL_PROMPT_END}]=> ") f"{RL_PROMPT_START}{self.COLOR_BRIGHT_BLUE}{RL_PROMPT_END}{display_path}"
f"{RL_PROMPT_START}{self.COLOR_RESET}{RL_PROMPT_END}]"
f"{RL_PROMPT_START}{self.COLOR_BRIGHT_CYAN}{RL_PROMPT_END}{RL_PROMPT_START}{self.COLOR_RESET}{RL_PROMPT_END}\n"
f"{RL_PROMPT_START}{self.COLOR_BRIGHT_CYAN}{RL_PROMPT_END}└─{RL_PROMPT_START}{self.COLOR_BRIGHT_MAGENTA}{RL_PROMPT_END}{RL_PROMPT_START}{self.COLOR_RESET}{RL_PROMPT_END} ")
return prompt return prompt
def cmd_help(self, args): def cmd_help(self, args):
"""Display available commands""" """Display available commands with enhanced formatting"""
print("\nZDTT Terminal Commands:")
print(" help - Display this help message")
print(" clear - Clear the screen")
print(" echo <message> - Echo a message")
print(" about - About ZDTT Terminal")
print(" history - Show command history")
print(" plugins [reload] - List or reload plugins")
print(" alias [name=cmd] - Create or display command aliases")
print(" unalias <name> - Remove an alias")
print(" zps install <url> - Install plugin from URL")
print(" time [options] - Display date/time (MM/DD/YY 12h default)")
print(" statusbar color <name> - Change status bar highlight color")
print(" update - Run the ZDTT updater helper")
print(" exit - Exit ZDTT (return to shell)")
print(" quit - Quit and close terminal window")
print() print()
print("File System Commands:") print(f"{self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}╔═══════════════════════════════════════════════════════════╗{self.COLOR_RESET}")
print(" ls [options] - List directory contents") print(f"{self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}{self.COLOR_RESET} {self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}ZDTT Terminal Commands{self.COLOR_RESET} {self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}{self.COLOR_RESET}")
print(" pwd - Print working directory") print(f"{self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}╚═══════════════════════════════════════════════════════════╝{self.COLOR_RESET}")
print(" cd <directory> - Change directory")
print(" cat <file> - Display file contents")
print(" mkdir <directory> - Create directory")
print(" touch <file> - Create empty file")
print(" rm [-rf] <file> - Remove file/directory (prompts without -f)")
print(" mv <src> <dest> - Move/rename file")
print(" cp [-r] <src> <dest> - Copy file")
print(" grep <pattern> <file> - Search for pattern in file")
print() print()
print("System Commands:") print(f"{self.COLOR_BRIGHT_MAGENTA}{self.COLOR_BOLD}Core Commands:{self.COLOR_RESET}")
print(" whoami - Display current user") print(f" {self.COLOR_BRIGHT_GREEN}help{self.COLOR_RESET} - Display this help message")
print(" date - Display current date/time") print(f" {self.COLOR_BRIGHT_GREEN}clear{self.COLOR_RESET} - Clear the screen")
print(" uname [options] - Display system information") print(f" {self.COLOR_BRIGHT_GREEN}echo{self.COLOR_RESET} <message> - Echo a message")
print(" nano <file> - Edit file with nano") print(f" {self.COLOR_BRIGHT_GREEN}about{self.COLOR_RESET} - About ZDTT Terminal")
print(" sysfetch - Display system info (prefers distro tools)") print(f" {self.COLOR_BRIGHT_GREEN}history{self.COLOR_RESET} - Show command history")
print(f" {self.COLOR_BRIGHT_GREEN}plugins{self.COLOR_RESET} [reload] - List or reload plugins")
print(f" {self.COLOR_BRIGHT_GREEN}alias{self.COLOR_RESET} [name=cmd] - Create or display command aliases")
print(f" {self.COLOR_BRIGHT_GREEN}unalias{self.COLOR_RESET} <name> - Remove an alias")
print(f" {self.COLOR_BRIGHT_GREEN}zps{self.COLOR_RESET} install <url> - Install plugin from URL")
print(f" {self.COLOR_BRIGHT_GREEN}time{self.COLOR_RESET} [options] - Display date/time (MM/DD/YY 12h default)")
print(f" {self.COLOR_BRIGHT_GREEN}statusbar{self.COLOR_RESET} color <name> - Change status bar highlight color")
print(f" {self.COLOR_BRIGHT_GREEN}update{self.COLOR_RESET} - Run the ZDTT updater helper")
print(f" {self.COLOR_BRIGHT_GREEN}exit{self.COLOR_RESET} - Exit ZDTT (return to shell)")
print(f" {self.COLOR_BRIGHT_GREEN}quit{self.COLOR_RESET} - Quit and close terminal window")
print() print()
print("Python Commands:") print(f"{self.COLOR_BRIGHT_MAGENTA}{self.COLOR_BOLD}File System Commands:{self.COLOR_RESET}")
print(" python [args] - Run Python interpreter") print(f" {self.COLOR_BRIGHT_GREEN}ls{self.COLOR_RESET} [options] - List directory contents")
print(" python3 [args] - Run Python 3 interpreter") print(f" {self.COLOR_BRIGHT_GREEN}pwd{self.COLOR_RESET} - Print working directory")
print(" pip [args] - Run pip package manager") print(f" {self.COLOR_BRIGHT_GREEN}cd{self.COLOR_RESET} <directory> - Change directory")
print(" pip3 [args] - Run pip3 package manager") print(f" {self.COLOR_BRIGHT_GREEN}cat{self.COLOR_RESET} <file> - Display file contents")
print(f" {self.COLOR_BRIGHT_GREEN}mkdir{self.COLOR_RESET} <directory> - Create directory")
print(f" {self.COLOR_BRIGHT_GREEN}touch{self.COLOR_RESET} <file> - Create empty file")
print(f" {self.COLOR_BRIGHT_GREEN}rm{self.COLOR_RESET} [-rf] <file> - Remove file/directory (prompts without -f)")
print(f" {self.COLOR_BRIGHT_GREEN}mv{self.COLOR_RESET} <src> <dest> - Move/rename file")
print(f" {self.COLOR_BRIGHT_GREEN}cp{self.COLOR_RESET} [-r] <src> <dest> - Copy file")
print(f" {self.COLOR_BRIGHT_GREEN}grep{self.COLOR_RESET} <pattern> <file> - Search for pattern in file")
print() print()
print("Features:") print(f"{self.COLOR_BRIGHT_MAGENTA}{self.COLOR_BOLD}System Commands:{self.COLOR_RESET}")
print(" ↑/↓ arrows - Navigate command history") print(f" {self.COLOR_BRIGHT_GREEN}whoami{self.COLOR_RESET} - Display current user")
print(" Tab - Auto-complete commands/files") print(f" {self.COLOR_BRIGHT_GREEN}date{self.COLOR_RESET} - Display current date/time")
print(" -oszdtt flag - Run any command in system shell") print(f" {self.COLOR_BRIGHT_GREEN}uname{self.COLOR_RESET} [options] - Display system information")
print(" Example: htop -oszdtt") print(f" {self.COLOR_BRIGHT_GREEN}nano{self.COLOR_RESET} <file> - Edit file with nano")
print(f" {self.COLOR_BRIGHT_GREEN}sysfetch{self.COLOR_RESET} - Display system info (prefers distro tools)")
print()
print(f"{self.COLOR_BRIGHT_MAGENTA}{self.COLOR_BOLD}Python Commands:{self.COLOR_RESET}")
print(f" {self.COLOR_BRIGHT_GREEN}python{self.COLOR_RESET} [args] - Run Python interpreter")
print(f" {self.COLOR_BRIGHT_GREEN}python3{self.COLOR_RESET} [args] - Run Python 3 interpreter")
print(f" {self.COLOR_BRIGHT_GREEN}pip{self.COLOR_RESET} [args] - Run pip package manager")
print(f" {self.COLOR_BRIGHT_GREEN}pip3{self.COLOR_RESET} [args] - Run pip3 package manager")
print()
print(f"{self.COLOR_BRIGHT_MAGENTA}{self.COLOR_BOLD}Features:{self.COLOR_RESET}")
print(f" {self.COLOR_BRIGHT_YELLOW}↑/↓ arrows{self.COLOR_RESET} - Navigate command history")
print(f" {self.COLOR_BRIGHT_YELLOW}Tab{self.COLOR_RESET} - Auto-complete commands/files")
print(f" {self.COLOR_BRIGHT_YELLOW}-oszdtt flag{self.COLOR_RESET} - Run any command in system shell")
print(f" {self.COLOR_DIM}Example: htop -oszdtt{self.COLOR_RESET}")
print() print()
def cmd_clear(self, args): def cmd_clear(self, args):
@@ -784,48 +948,55 @@ ZDTT Terminal v{self.version}
sys.exit(0) sys.exit(0)
def cmd_about(self, args): def cmd_about(self, args):
"""Display information about ZDTT Terminal""" """Display information about ZDTT Terminal with enhanced formatting"""
print(f"\nZDTT Terminal v{self.version}") print()
print("A custom terminal interface for Debian-based and Arch Linux systems") print(f"{self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}╔═══════════════════════════════════════════════════════════╗{self.COLOR_RESET}")
print(f"{self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}{self.COLOR_RESET} {self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}About ZDTT Terminal{self.COLOR_RESET} {self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}{self.COLOR_RESET}")
print(f"{self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}╚═══════════════════════════════════════════════════════════╝{self.COLOR_RESET}")
print()
print(f" {self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}Version:{self.COLOR_RESET} {self.COLOR_BRIGHT_WHITE}v{self.version}{self.COLOR_RESET}")
print(f" {self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}Description:{self.COLOR_RESET} A custom terminal interface for Debian-based and Arch Linux systems")
print()
# Show distribution status # Show distribution status with colors
print(f" {self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}System Status:{self.COLOR_RESET}")
if self.is_debian: if self.is_debian:
print("Running on: Debian-based system (fully supported)") print(f" {self.COLOR_BRIGHT_GREEN}{self.COLOR_RESET} Debian-based system {self.COLOR_BRIGHT_GREEN}(fully supported){self.COLOR_RESET}")
elif self.is_arch: elif self.is_arch:
print("Running on: Arch Linux (fully supported)") print(f" {self.COLOR_BRIGHT_GREEN}{self.COLOR_RESET} Arch Linux {self.COLOR_BRIGHT_GREEN}(fully supported){self.COLOR_RESET}")
else: else:
print("Running on: Unsupported system (limited support)") print(f" {self.COLOR_WARNING}{self.COLOR_RESET} Unsupported system {self.COLOR_WARNING}(limited support){self.COLOR_RESET}")
print() print()
print("Features:") print(f" {self.COLOR_BRIGHT_MAGENTA}{self.COLOR_BOLD}Features:{self.COLOR_RESET}")
print(" Automatic update checking on startup") print(f" {self.COLOR_BRIGHT_GREEN}{self.COLOR_RESET} Automatic update checking on startup")
print(" Command history with ↑/↓ navigation (1000 commands)") print(f" {self.COLOR_BRIGHT_GREEN}{self.COLOR_RESET} Command history with ↑/↓ navigation (1000 commands)")
print(" Tab completion for commands and files") print(f" {self.COLOR_BRIGHT_GREEN}{self.COLOR_RESET} Tab completion for commands and files")
print(" Command aliases (alias g=git)") print(f" {self.COLOR_BRIGHT_GREEN}{self.COLOR_RESET} Command aliases (alias g=git)")
print(" Flexible time/date display with multiple formats") print(f" {self.COLOR_BRIGHT_GREEN}{self.COLOR_RESET} Flexible time/date display with multiple formats")
print(" • Colorized prompt") print(f" {self.COLOR_BRIGHT_GREEN}{self.COLOR_RESET} Colorized prompt with enhanced styling")
print(" Smart banner (auto-hides on small terminals)") print(f" {self.COLOR_BRIGHT_GREEN}{self.COLOR_RESET} Smart banner (auto-hides on small terminals)")
print(" Plugin system with ZPS package manager") print(f" {self.COLOR_BRIGHT_GREEN}{self.COLOR_RESET} Plugin system with ZPS package manager")
print(" Plugin hot-reload (plugins reload)") print(f" {self.COLOR_BRIGHT_GREEN}{self.COLOR_RESET} Plugin hot-reload (plugins reload)")
print(" Safe rm with confirmation prompts") print(f" {self.COLOR_BRIGHT_GREEN}{self.COLOR_RESET} Safe rm with confirmation prompts")
print(" Custom banner support (~/.zdtt/banner.txt)") print(f" {self.COLOR_BRIGHT_GREEN}{self.COLOR_RESET} Custom banner support (~/.zdtt/banner.txt)")
print(" Native command support") print(f" {self.COLOR_BRIGHT_GREEN}{self.COLOR_RESET} Native command support")
print(" System command execution via -oszdtt flag") print(f" {self.COLOR_BRIGHT_GREEN}{self.COLOR_RESET} System command execution via -oszdtt flag")
print(" Clean, premium interface") print(f" {self.COLOR_BRIGHT_GREEN}{self.COLOR_RESET} Clean, premium interface")
print() print()
print("Configuration:") print(f" {self.COLOR_BRIGHT_MAGENTA}{self.COLOR_BOLD}Configuration:{self.COLOR_RESET}")
print(f" • ZDTT directory: {self.zdtt_dir}") print(f" {self.COLOR_DIM}{self.COLOR_RESET} ZDTT directory: {self.COLOR_BRIGHT_CYAN}{self.zdtt_dir}{self.COLOR_RESET}")
print(f" • Aliases: {self.aliases_file}") print(f" {self.COLOR_DIM}{self.COLOR_RESET} Aliases: {self.COLOR_BRIGHT_CYAN}{self.aliases_file}{self.COLOR_RESET}")
print(f" • Custom banner: {self.banner_file}") print(f" {self.COLOR_DIM}{self.COLOR_RESET} Custom banner: {self.COLOR_BRIGHT_CYAN}{self.banner_file}{self.COLOR_RESET}")
print(f" • Plugin errors: {self.log_file}") print(f" {self.COLOR_DIM}{self.COLOR_RESET} Plugin errors: {self.COLOR_BRIGHT_CYAN}{self.log_file}{self.COLOR_RESET}")
print() print()
def cmd_history(self, args): def cmd_history(self, args):
"""Display command history""" """Display command history with enhanced formatting"""
history_length = readline.get_current_history_length() history_length = readline.get_current_history_length()
if history_length == 0: if history_length == 0:
print("No history available") print(f"{self.COLOR_WARNING}No history available{self.COLOR_RESET}")
return return
# Show last 50 commands by default # Show last 50 commands by default
@@ -836,17 +1007,20 @@ ZDTT Terminal v{self.version}
start = max(1, history_length - limit + 1) start = max(1, history_length - limit + 1)
print() print()
print(f"{self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}Command History:{self.COLOR_RESET} (showing {limit} of {history_length} commands)")
print(f"{self.COLOR_DIM}{'' * 60}{self.COLOR_RESET}")
for i in range(start, history_length + 1): for i in range(start, history_length + 1):
cmd = readline.get_history_item(i) cmd = readline.get_history_item(i)
if cmd: if cmd:
print(f"{i:4d} {cmd}") print(f"{self.COLOR_BRIGHT_BLACK}{i:4d}{self.COLOR_RESET} {self.COLOR_BRIGHT_CYAN}{cmd}{self.COLOR_RESET}")
print(f"{self.COLOR_DIM}{'' * 60}{self.COLOR_RESET}")
print() print()
def cmd_plugins(self, args): def cmd_plugins(self, args):
"""List or reload plugins""" """List or reload plugins"""
# Check for reload subcommand # Check for reload subcommand
if args and args[0] == 'reload': if args and args[0] == 'reload':
print("Reloading plugins...") print(f"{self.COLOR_BRIGHT_CYAN}Reloading plugins...{self.COLOR_RESET}")
# Remove plugin commands and reload aliases to avoid conflicts # Remove plugin commands and reload aliases to avoid conflicts
self.unload_plugin_commands() self.unload_plugin_commands()
self.aliases.clear() self.aliases.clear()
@@ -854,7 +1028,7 @@ ZDTT Terminal v{self.version}
# Reload plugins # Reload plugins
self.load_plugins() self.load_plugins()
print("✓ Plugins reloaded successfully!") print(f"{self.COLOR_BRIGHT_GREEN}✓ Plugins reloaded successfully!{self.COLOR_RESET}")
print() print()
return return
@@ -862,24 +1036,33 @@ ZDTT Terminal v{self.version}
plugin_files = glob.glob(os.path.join(self.plugin_dir, "*.py")) plugin_files = glob.glob(os.path.join(self.plugin_dir, "*.py"))
if not plugin_files: if not plugin_files:
print("\nNo plugins installed.") print()
print(f"Plugin directory: {self.plugin_dir}") print(f"{self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}Plugins:{self.COLOR_RESET}")
print("\nTo create a plugin, create a .py file with a register_commands() function") print(f"{self.COLOR_DIM}{'' * 60}{self.COLOR_RESET}")
print("that returns a dictionary of command names to functions.") print(f"{self.COLOR_WARNING}No plugins installed.{self.COLOR_RESET}")
print("\nOr use: zps install <url> to install from a URL") print()
print(f"Plugin directory: {self.COLOR_BRIGHT_CYAN}{self.plugin_dir}{self.COLOR_RESET}")
print()
print(f"{self.COLOR_DIM}To create a plugin, create a .py file with a register_commands() function{self.COLOR_RESET}")
print(f"{self.COLOR_DIM}that returns a dictionary of command names to functions.{self.COLOR_RESET}")
print()
print(f"Or use: {self.COLOR_BRIGHT_GREEN}zps install <url>{self.COLOR_RESET} to install from a URL")
print() print()
return return
print(f"\nLoaded Plugins ({len(plugin_files)}):") print()
print(f"{self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}Loaded Plugins:{self.COLOR_RESET} {self.COLOR_BRIGHT_GREEN}({len(plugin_files)}){self.COLOR_RESET}")
print(f"{self.COLOR_DIM}{'' * 60}{self.COLOR_RESET}")
for plugin_file in plugin_files: for plugin_file in plugin_files:
plugin_name = os.path.basename(plugin_file)[:-3] plugin_name = os.path.basename(plugin_file)[:-3]
print(f" {plugin_name}") print(f" {self.COLOR_BRIGHT_GREEN}{self.COLOR_RESET} {self.COLOR_BRIGHT_CYAN}{plugin_name}{self.COLOR_RESET}")
print(f"{self.COLOR_DIM}{'' * 60}{self.COLOR_RESET}")
print() print()
print(f"Plugin directory: {self.plugin_dir}") print(f"Plugin directory: {self.COLOR_BRIGHT_CYAN}{self.plugin_dir}{self.COLOR_RESET}")
print(f"Error log: {self.log_file}") print(f"Error log: {self.COLOR_BRIGHT_CYAN}{self.log_file}{self.COLOR_RESET}")
print() print()
print("Commands:") print(f"{self.COLOR_BRIGHT_MAGENTA}Commands:{self.COLOR_RESET}")
print(" plugins reload - Reload all plugins without restarting") print(f" {self.COLOR_BRIGHT_GREEN}plugins reload{self.COLOR_RESET} - Reload all plugins without restarting")
print() print()
def cmd_alias(self, args): def cmd_alias(self, args):
@@ -887,14 +1070,21 @@ ZDTT Terminal v{self.version}
if not args: if not args:
# Display all aliases # Display all aliases
if not self.aliases: if not self.aliases:
print("\nNo aliases defined.") print()
print("Usage: alias name=command") print(f"{self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}Aliases:{self.COLOR_RESET}")
print("Example: alias g=git") print(f"{self.COLOR_DIM}{'' * 60}{self.COLOR_RESET}")
print(f"{self.COLOR_WARNING}No aliases defined.{self.COLOR_RESET}")
print()
print(f"Usage: {self.COLOR_BRIGHT_GREEN}alias name=command{self.COLOR_RESET}")
print(f"Example: {self.COLOR_BRIGHT_GREEN}alias g=git{self.COLOR_RESET}")
print() print()
else: else:
print("\nDefined Aliases:") print()
print(f"{self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}Defined Aliases:{self.COLOR_RESET} {self.COLOR_BRIGHT_GREEN}({len(self.aliases)}){self.COLOR_RESET}")
print(f"{self.COLOR_DIM}{'' * 60}{self.COLOR_RESET}")
for name, command in sorted(self.aliases.items()): for name, command in sorted(self.aliases.items()):
print(f" {name}={command}") print(f" {self.COLOR_BRIGHT_GREEN}{name}{self.COLOR_RESET}={self.COLOR_BRIGHT_CYAN}{command}{self.COLOR_RESET}")
print(f"{self.COLOR_DIM}{'' * 60}{self.COLOR_RESET}")
print() print()
return return
@@ -975,7 +1165,7 @@ ZDTT Terminal v{self.version}
# Validate it's a .py file # Validate it's a .py file
if not filename.endswith('.py'): if not filename.endswith('.py'):
print(f"Error: '{filename}' is not a Python file") print(f"{self.COLOR_ERROR}Error: '{filename}' is not a Python file{self.COLOR_RESET}")
print("Plugin URLs must end with .py") print("Plugin URLs must end with .py")
return return
@@ -1002,7 +1192,7 @@ ZDTT Terminal v{self.version}
with open(target_path, 'wb') as f: with open(target_path, 'wb') as f:
f.write(plugin_content) f.write(plugin_content)
print(f"✓ Plugin '{filename}' installed successfully!") print(f"{self.COLOR_BRIGHT_GREEN}✓ Plugin '{filename}' installed successfully!{self.COLOR_RESET}")
print(f" Location: {target_path}") print(f" Location: {target_path}")
print() print()
print("To use the plugin:") print("To use the plugin:")
@@ -1011,13 +1201,13 @@ ZDTT Terminal v{self.version}
print() print()
except urllib.error.HTTPError as e: except urllib.error.HTTPError as e:
print(f"Error: Failed to download plugin (HTTP {e.code})") print(f"{self.COLOR_ERROR}Error: Failed to download plugin (HTTP {e.code}){self.COLOR_RESET}")
print(f"URL: {url}") print(f"URL: {url}")
except urllib.error.URLError as e: except urllib.error.URLError as e:
print(f"Error: Failed to connect to server") print(f"{self.COLOR_ERROR}Error: Failed to connect to server{self.COLOR_RESET}")
print(f"Reason: {e.reason}") print(f"Reason: {e.reason}")
except Exception as e: except Exception as e:
print(f"Error: {e}") print(f"{self.COLOR_ERROR}Error: {e}{self.COLOR_RESET}")
return return
@@ -1085,7 +1275,7 @@ ZDTT Terminal v{self.version}
try: try:
print(now.strftime(custom_format)) print(now.strftime(custom_format))
except Exception as e: except Exception as e:
print(f"Error: Invalid format string - {e}") print(f"{self.COLOR_ERROR}Error: Invalid format string - {e}{self.COLOR_RESET}")
return return
# Default format: MM/DD/YY with time # Default format: MM/DD/YY with time
@@ -1125,7 +1315,7 @@ ZDTT Terminal v{self.version}
self.status_bar_color = color self.status_bar_color = color
self.save_preferences() self.save_preferences()
self._render_status_bar() self._render_status_bar()
print(f"Status bar color updated to {color}.") print(f"{self.COLOR_BRIGHT_GREEN}{self.COLOR_RESET} Status bar color updated to {self.COLOR_BRIGHT_CYAN}{color}{self.COLOR_RESET}.")
def cmd_echo(self, args): def cmd_echo(self, args):
"""Echo the provided arguments""" """Echo the provided arguments"""
@@ -1502,12 +1692,20 @@ ZDTT Terminal v{self.version}
if cmd in self.commands: if cmd in self.commands:
self.commands[cmd](args) self.commands[cmd](args)
else: else:
print(f"Command not found: {cmd}") print(f"{self.COLOR_ERROR}Command not found: {self.COLOR_BRIGHT_RED}{cmd}{self.COLOR_RESET}")
print("Type 'help' for available commands.") print(f"Type {self.COLOR_BRIGHT_GREEN}'help'{self.COLOR_RESET} for available commands.")
print("Tip: Use -oszdtt flag to run system commands") print(f"{self.COLOR_DIM}Tip: Use -oszdtt flag to run system commands{self.COLOR_RESET}")
def run(self): def run(self):
"""Main terminal loop""" """Main terminal loop"""
# Setup signal handler for terminal resize (SIGWINCH)
if sys.platform != 'win32':
try:
signal.signal(signal.SIGWINCH, self._handle_resize)
except (AttributeError, ValueError):
# SIGWINCH not available on this platform
pass
# 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.initialize_status_bar()

View File

@@ -1 +1 @@
0.1.2.a.2 0.1.2.a.3