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:
@@ -45,7 +45,7 @@
|
||||
<div class="hero__metrics">
|
||||
<div class="metric">
|
||||
<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 class="metric">
|
||||
<p class="metric__label">Supported families</p>
|
||||
|
||||
42
script.js
42
script.js
@@ -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);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
340
styles.css
340
styles.css
@@ -4,12 +4,14 @@
|
||||
--bg: #05060a;
|
||||
--bg-alt: #0e1018;
|
||||
--card: #121422;
|
||||
--card-border: rgba(255, 255, 255, 0.08);
|
||||
--card-border: rgba(255, 255, 255, 0.12);
|
||||
--text: #f5f6fd;
|
||||
--muted: #9aa2c4;
|
||||
--accent: #4dd5ff;
|
||||
--accent-strong: #6d84ff;
|
||||
--accent-glow: rgba(77, 213, 255, 0.4);
|
||||
--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;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
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);
|
||||
background:
|
||||
radial-gradient(circle at 0% 0%, rgba(77, 213, 255, 0.15), transparent 50%),
|
||||
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);
|
||||
font-size: 1rem;
|
||||
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 {
|
||||
@@ -56,6 +86,27 @@ button:focus-visible {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
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 {
|
||||
@@ -68,6 +119,12 @@ button:focus-visible {
|
||||
.brand {
|
||||
font-weight: 700;
|
||||
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 {
|
||||
@@ -79,14 +136,32 @@ button:focus-visible {
|
||||
.nav__links a {
|
||||
color: var(--muted);
|
||||
font-weight: 500;
|
||||
transition: color 0.2s ease;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
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 {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.nav__links a:hover::after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav__toggle {
|
||||
display: none;
|
||||
background: none;
|
||||
@@ -119,6 +194,11 @@ h1 {
|
||||
font-size: clamp(2.7rem, 5vw, 4.8rem);
|
||||
margin: 0 0 1rem;
|
||||
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 {
|
||||
@@ -149,8 +229,28 @@ h3 {
|
||||
border-radius: 999px;
|
||||
font-weight: 600;
|
||||
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;
|
||||
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 {
|
||||
@@ -163,32 +263,51 @@ h3 {
|
||||
}
|
||||
|
||||
.btn.primary {
|
||||
background: linear-gradient(90deg, var(--accent-strong), var(--accent));
|
||||
background: linear-gradient(135deg, var(--accent-strong), var(--accent));
|
||||
color: #05060a;
|
||||
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 {
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
border-color: rgba(255, 255, 255, 0.25);
|
||||
color: var(--text);
|
||||
background: transparent;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
backdrop-filter: blur(10px);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 12px 30px rgba(109, 132, 255, 0.25);
|
||||
.btn.ghost:hover {
|
||||
transform: translateY(-2px);
|
||||
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 {
|
||||
display: block;
|
||||
padding: 1rem 1.25rem;
|
||||
border-radius: 0.75rem;
|
||||
background: rgba(18, 20, 34, 0.7);
|
||||
background: rgba(18, 20, 34, 0.8);
|
||||
border: 1px solid var(--card-border);
|
||||
font-family: 'JetBrains Mono', 'Space Grotesk', monospace;
|
||||
font-size: 0.95rem;
|
||||
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 {
|
||||
@@ -201,7 +320,32 @@ h3 {
|
||||
padding: 1.25rem;
|
||||
border-radius: 1rem;
|
||||
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 {
|
||||
@@ -239,6 +383,40 @@ h3 {
|
||||
background: var(--card);
|
||||
box-shadow: var(--shadow);
|
||||
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 {
|
||||
@@ -262,10 +440,19 @@ h3 {
|
||||
padding: 1.5rem;
|
||||
border-radius: 1rem;
|
||||
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;
|
||||
position: relative;
|
||||
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 {
|
||||
@@ -281,6 +468,14 @@ h3 {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
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 {
|
||||
@@ -289,6 +484,14 @@ h3 {
|
||||
border-radius: 1rem;
|
||||
background: rgba(255, 196, 87, 0.12);
|
||||
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 {
|
||||
@@ -303,7 +506,33 @@ figure {
|
||||
border-radius: 1rem;
|
||||
padding: 1rem;
|
||||
background: rgba(9, 10, 18, 0.8);
|
||||
backdrop-filter: blur(10px);
|
||||
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 {
|
||||
@@ -313,22 +542,55 @@ figcaption {
|
||||
}
|
||||
|
||||
pre {
|
||||
background: rgba(5, 6, 10, 0.65);
|
||||
background: rgba(5, 6, 10, 0.8);
|
||||
padding: 1rem;
|
||||
border-radius: 0.75rem;
|
||||
overflow: auto;
|
||||
font-size: 0.85rem;
|
||||
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 {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
background: linear-gradient(90deg, var(--accent-strong), var(--accent));
|
||||
background: linear-gradient(135deg, var(--accent-strong), var(--accent));
|
||||
color: #05060a;
|
||||
padding: 0.65rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
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 {
|
||||
@@ -340,7 +602,7 @@ pre {
|
||||
|
||||
.footer {
|
||||
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;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
@@ -348,6 +610,28 @@ pre {
|
||||
gap: 1rem;
|
||||
font-size: 0.9rem;
|
||||
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) {
|
||||
@@ -365,9 +649,27 @@ pre {
|
||||
flex-direction: column;
|
||||
padding: 1rem 0 0;
|
||||
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 {
|
||||
display: flex;
|
||||
animation: slideDown 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
462
terminal.py
462
terminal.py
@@ -16,6 +16,7 @@ import logging
|
||||
import threading
|
||||
import json
|
||||
import shlex
|
||||
import signal
|
||||
from datetime import datetime
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
@@ -218,6 +219,7 @@ class ZDTTTerminal:
|
||||
self.scroll_region_set = False
|
||||
self.plugin_command_names = set()
|
||||
self.update_check_thread = None
|
||||
self.resize_lock = threading.Lock() # Lock for resize operations
|
||||
|
||||
# Setup logging for plugins
|
||||
self.setup_logging()
|
||||
@@ -232,12 +234,50 @@ class ZDTTTerminal:
|
||||
# Load user preferences (status bar color, etc.)
|
||||
self.load_preferences()
|
||||
|
||||
# ANSI color codes
|
||||
# ANSI color codes - Enhanced palette
|
||||
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_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 = {
|
||||
'help': self.cmd_help,
|
||||
@@ -473,35 +513,106 @@ ZDTT Terminal v{self.version}
|
||||
|
||||
def _render_status_bar(self):
|
||||
"""Render a single-line status bar with branding and time."""
|
||||
bar_text = self._build_status_bar_text()
|
||||
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[1;1H") # Move to first row
|
||||
sys.stdout.write("\033[2K") # Clear the line
|
||||
sys.stdout.write("\033[1;1H") # Move to first row, first column
|
||||
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(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.flush()
|
||||
except Exception:
|
||||
print(bar_text)
|
||||
# Fallback: just skip rendering if there's an error
|
||||
pass
|
||||
|
||||
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
|
||||
"""Render a single-line status bar with enhanced branding and time."""
|
||||
left_text = f"{self.COLOR_BOLD}ZDTT{self.COLOR_RESET} by {self.COLOR_BOLD}ZaneDev{self.COLOR_RESET}"
|
||||
time_str = datetime.now().strftime("%I:%M %p")
|
||||
plain_left = "ZDTT by ZaneDev"
|
||||
plain_time = time_str
|
||||
|
||||
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)
|
||||
try:
|
||||
# Always get fresh terminal size to handle resizes
|
||||
term_size = shutil.get_terminal_size()
|
||||
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:
|
||||
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'))
|
||||
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):
|
||||
"""Reserve the top row for the status bar."""
|
||||
@@ -525,6 +636,50 @@ ZDTT Terminal v{self.version}
|
||||
sys.stdout.flush()
|
||||
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):
|
||||
"""Setup readline for history and tab completion"""
|
||||
# Setup history
|
||||
@@ -626,7 +781,7 @@ ZDTT Terminal v{self.version}
|
||||
|
||||
# Show brief status if there were failures
|
||||
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):
|
||||
"""Remove commands that originated from plugins."""
|
||||
@@ -668,7 +823,7 @@ ZDTT Terminal v{self.version}
|
||||
f.write(f"{name}={command}\n")
|
||||
except Exception as 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):
|
||||
"""Expand aliases in command line"""
|
||||
@@ -689,7 +844,7 @@ ZDTT Terminal v{self.version}
|
||||
return command_line
|
||||
|
||||
def get_prompt(self):
|
||||
"""Return the custom prompt string with colors"""
|
||||
"""Return the custom prompt string with enhanced colors"""
|
||||
# Show current directory in prompt
|
||||
cwd = os.getcwd()
|
||||
# Show ~ for home directory
|
||||
@@ -704,63 +859,72 @@ ZDTT Terminal v{self.version}
|
||||
RL_PROMPT_START = '\001'
|
||||
RL_PROMPT_END = '\002'
|
||||
|
||||
# Create colorized prompt with readline-safe escape codes
|
||||
# [username in green @ ZDTT path in blue]=>
|
||||
prompt = (f"[{RL_PROMPT_START}{self.COLOR_GREEN}{RL_PROMPT_END}{self.username}"
|
||||
# Create enhanced colorized prompt with gradient-like effect
|
||||
# [username in bright green @ ZDTT in bright cyan path in bright blue]=>
|
||||
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_CYAN}{RL_PROMPT_END}ZDTT{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_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_BRIGHT_CYAN}{RL_PROMPT_END}ZDTT{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
|
||||
|
||||
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 <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")
|
||||
"""Display available commands with enhanced formatting"""
|
||||
print()
|
||||
print("File System Commands:")
|
||||
print(" ls [options] - List directory contents")
|
||||
print(" pwd - Print working directory")
|
||||
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(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}ZDTT Terminal Commands{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("System Commands:")
|
||||
print(" whoami - Display current user")
|
||||
print(" date - Display current date/time")
|
||||
print(" uname [options] - Display system information")
|
||||
print(" nano <file> - Edit file with nano")
|
||||
print(" sysfetch - Display system info (prefers distro tools)")
|
||||
print(f"{self.COLOR_BRIGHT_MAGENTA}{self.COLOR_BOLD}Core Commands:{self.COLOR_RESET}")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}help{self.COLOR_RESET} - Display this help message")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}clear{self.COLOR_RESET} - Clear the screen")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}echo{self.COLOR_RESET} <message> - Echo a message")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}about{self.COLOR_RESET} - About ZDTT Terminal")
|
||||
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("Python Commands:")
|
||||
print(" python [args] - Run Python interpreter")
|
||||
print(" python3 [args] - Run Python 3 interpreter")
|
||||
print(" pip [args] - Run pip package manager")
|
||||
print(" pip3 [args] - Run pip3 package manager")
|
||||
print(f"{self.COLOR_BRIGHT_MAGENTA}{self.COLOR_BOLD}File System Commands:{self.COLOR_RESET}")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}ls{self.COLOR_RESET} [options] - List directory contents")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}pwd{self.COLOR_RESET} - Print working directory")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}cd{self.COLOR_RESET} <directory> - Change directory")
|
||||
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("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(f"{self.COLOR_BRIGHT_MAGENTA}{self.COLOR_BOLD}System Commands:{self.COLOR_RESET}")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}whoami{self.COLOR_RESET} - Display current user")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}date{self.COLOR_RESET} - Display current date/time")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}uname{self.COLOR_RESET} [options] - Display system information")
|
||||
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()
|
||||
|
||||
def cmd_clear(self, args):
|
||||
@@ -784,48 +948,55 @@ ZDTT Terminal v{self.version}
|
||||
sys.exit(0)
|
||||
|
||||
def cmd_about(self, args):
|
||||
"""Display information about ZDTT Terminal"""
|
||||
print(f"\nZDTT Terminal v{self.version}")
|
||||
print("A custom terminal interface for Debian-based and Arch Linux systems")
|
||||
"""Display information about ZDTT Terminal with enhanced formatting"""
|
||||
print()
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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("Features:")
|
||||
print(" • Automatic update checking on startup")
|
||||
print(" • Command history with ↑/↓ navigation (1000 commands)")
|
||||
print(" • Tab completion for commands and files")
|
||||
print(" • Command aliases (alias g=git)")
|
||||
print(" • Flexible time/date display with multiple formats")
|
||||
print(" • Colorized prompt")
|
||||
print(" • Smart banner (auto-hides on small terminals)")
|
||||
print(" • Plugin system with ZPS package manager")
|
||||
print(" • Plugin hot-reload (plugins reload)")
|
||||
print(" • Safe rm with confirmation prompts")
|
||||
print(" • Custom banner support (~/.zdtt/banner.txt)")
|
||||
print(" • Native command support")
|
||||
print(" • System command execution via -oszdtt flag")
|
||||
print(" • Clean, premium interface")
|
||||
print(f" {self.COLOR_BRIGHT_MAGENTA}{self.COLOR_BOLD}Features:{self.COLOR_RESET}")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}•{self.COLOR_RESET} Automatic update checking on startup")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}•{self.COLOR_RESET} Command history with ↑/↓ navigation (1000 commands)")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}•{self.COLOR_RESET} Tab completion for commands and files")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}•{self.COLOR_RESET} Command aliases (alias g=git)")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}•{self.COLOR_RESET} Flexible time/date display with multiple formats")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}•{self.COLOR_RESET} Colorized prompt with enhanced styling")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}•{self.COLOR_RESET} Smart banner (auto-hides on small terminals)")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}•{self.COLOR_RESET} Plugin system with ZPS package manager")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}•{self.COLOR_RESET} Plugin hot-reload (plugins reload)")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}•{self.COLOR_RESET} Safe rm with confirmation prompts")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}•{self.COLOR_RESET} Custom banner support (~/.zdtt/banner.txt)")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}•{self.COLOR_RESET} Native command support")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}•{self.COLOR_RESET} System command execution via -oszdtt flag")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}•{self.COLOR_RESET} Clean, premium interface")
|
||||
print()
|
||||
print("Configuration:")
|
||||
print(f" • ZDTT directory: {self.zdtt_dir}")
|
||||
print(f" • Aliases: {self.aliases_file}")
|
||||
print(f" • Custom banner: {self.banner_file}")
|
||||
print(f" • Plugin errors: {self.log_file}")
|
||||
print(f" {self.COLOR_BRIGHT_MAGENTA}{self.COLOR_BOLD}Configuration:{self.COLOR_RESET}")
|
||||
print(f" {self.COLOR_DIM}•{self.COLOR_RESET} ZDTT directory: {self.COLOR_BRIGHT_CYAN}{self.zdtt_dir}{self.COLOR_RESET}")
|
||||
print(f" {self.COLOR_DIM}•{self.COLOR_RESET} Aliases: {self.COLOR_BRIGHT_CYAN}{self.aliases_file}{self.COLOR_RESET}")
|
||||
print(f" {self.COLOR_DIM}•{self.COLOR_RESET} Custom banner: {self.COLOR_BRIGHT_CYAN}{self.banner_file}{self.COLOR_RESET}")
|
||||
print(f" {self.COLOR_DIM}•{self.COLOR_RESET} Plugin errors: {self.COLOR_BRIGHT_CYAN}{self.log_file}{self.COLOR_RESET}")
|
||||
print()
|
||||
|
||||
def cmd_history(self, args):
|
||||
"""Display command history"""
|
||||
"""Display command history with enhanced formatting"""
|
||||
history_length = readline.get_current_history_length()
|
||||
|
||||
if history_length == 0:
|
||||
print("No history available")
|
||||
print(f"{self.COLOR_WARNING}No history available{self.COLOR_RESET}")
|
||||
return
|
||||
|
||||
# Show last 50 commands by default
|
||||
@@ -836,17 +1007,20 @@ ZDTT Terminal v{self.version}
|
||||
start = max(1, history_length - limit + 1)
|
||||
|
||||
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):
|
||||
cmd = readline.get_history_item(i)
|
||||
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()
|
||||
|
||||
def cmd_plugins(self, args):
|
||||
"""List or reload plugins"""
|
||||
# Check for reload subcommand
|
||||
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
|
||||
self.unload_plugin_commands()
|
||||
self.aliases.clear()
|
||||
@@ -854,7 +1028,7 @@ ZDTT Terminal v{self.version}
|
||||
|
||||
# Reload plugins
|
||||
self.load_plugins()
|
||||
print("✓ Plugins reloaded successfully!")
|
||||
print(f"{self.COLOR_BRIGHT_GREEN}✓ Plugins reloaded successfully!{self.COLOR_RESET}")
|
||||
print()
|
||||
return
|
||||
|
||||
@@ -862,24 +1036,33 @@ ZDTT Terminal v{self.version}
|
||||
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("\nOr use: zps install <url> to install from a URL")
|
||||
print()
|
||||
print(f"{self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}Plugins:{self.COLOR_RESET}")
|
||||
print(f"{self.COLOR_DIM}{'─' * 60}{self.COLOR_RESET}")
|
||||
print(f"{self.COLOR_WARNING}No plugins installed.{self.COLOR_RESET}")
|
||||
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()
|
||||
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:
|
||||
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(f"Plugin directory: {self.plugin_dir}")
|
||||
print(f"Error log: {self.log_file}")
|
||||
print(f"Plugin directory: {self.COLOR_BRIGHT_CYAN}{self.plugin_dir}{self.COLOR_RESET}")
|
||||
print(f"Error log: {self.COLOR_BRIGHT_CYAN}{self.log_file}{self.COLOR_RESET}")
|
||||
print()
|
||||
print("Commands:")
|
||||
print(" plugins reload - Reload all plugins without restarting")
|
||||
print(f"{self.COLOR_BRIGHT_MAGENTA}Commands:{self.COLOR_RESET}")
|
||||
print(f" {self.COLOR_BRIGHT_GREEN}plugins reload{self.COLOR_RESET} - Reload all plugins without restarting")
|
||||
print()
|
||||
|
||||
def cmd_alias(self, args):
|
||||
@@ -887,14 +1070,21 @@ ZDTT Terminal v{self.version}
|
||||
if not args:
|
||||
# Display all aliases
|
||||
if not self.aliases:
|
||||
print("\nNo aliases defined.")
|
||||
print("Usage: alias name=command")
|
||||
print("Example: alias g=git")
|
||||
print()
|
||||
print(f"{self.COLOR_BRIGHT_CYAN}{self.COLOR_BOLD}Aliases:{self.COLOR_RESET}")
|
||||
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()
|
||||
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()):
|
||||
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()
|
||||
return
|
||||
|
||||
@@ -975,7 +1165,7 @@ ZDTT Terminal v{self.version}
|
||||
|
||||
# Validate it's a .py file
|
||||
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")
|
||||
return
|
||||
|
||||
@@ -1002,7 +1192,7 @@ ZDTT Terminal v{self.version}
|
||||
with open(target_path, 'wb') as f:
|
||||
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()
|
||||
print("To use the plugin:")
|
||||
@@ -1011,13 +1201,13 @@ ZDTT Terminal v{self.version}
|
||||
print()
|
||||
|
||||
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}")
|
||||
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}")
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
print(f"{self.COLOR_ERROR}Error: {e}{self.COLOR_RESET}")
|
||||
|
||||
return
|
||||
|
||||
@@ -1085,7 +1275,7 @@ ZDTT Terminal v{self.version}
|
||||
try:
|
||||
print(now.strftime(custom_format))
|
||||
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
|
||||
|
||||
# Default format: MM/DD/YY with time
|
||||
@@ -1125,7 +1315,7 @@ ZDTT Terminal v{self.version}
|
||||
self.status_bar_color = color
|
||||
self.save_preferences()
|
||||
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):
|
||||
"""Echo the provided arguments"""
|
||||
@@ -1502,12 +1692,20 @@ ZDTT Terminal v{self.version}
|
||||
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")
|
||||
print(f"{self.COLOR_ERROR}Command not found: {self.COLOR_BRIGHT_RED}{cmd}{self.COLOR_RESET}")
|
||||
print(f"Type {self.COLOR_BRIGHT_GREEN}'help'{self.COLOR_RESET} for available commands.")
|
||||
print(f"{self.COLOR_DIM}Tip: Use -oszdtt flag to run system commands{self.COLOR_RESET}")
|
||||
|
||||
def run(self):
|
||||
"""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
|
||||
os.system('clear' if os.name != 'nt' else 'cls')
|
||||
self.initialize_status_bar()
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.2.a.2
|
||||
0.1.2.a.3
|
||||
Reference in New Issue
Block a user