Add Spectre-style boot intro with decrypt animation + version system
Animated splash: boot lines with system detection, ASCII logo with character-by-character decrypt effect. --no-intro to skip. v1.0.0. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
162
systats.py
162
systats.py
@@ -1214,9 +1214,166 @@ class Renderer:
|
|||||||
curses.color_pair(C_POPUP_HL) | curses.A_BOLD)
|
curses.color_pair(C_POPUP_HL) | curses.A_BOLD)
|
||||||
|
|
||||||
|
|
||||||
|
# ─── Intro ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
VERSION = "1.0.0"
|
||||||
|
|
||||||
|
LOGO_ART = [
|
||||||
|
" ███████╗██╗ ██╗███████╗████████╗ █████╗ ████████╗███████╗",
|
||||||
|
" ██╔════╝╚██╗ ██╔╝██╔════╝╚══██╔══╝██╔══██╗╚══██╔══╝██╔════╝",
|
||||||
|
" ███████╗ ╚████╔╝ ███████╗ ██║ ███████║ ██║ ███████╗",
|
||||||
|
" ╚════██║ ╚██╔╝ ╚════██║ ██║ ██╔══██║ ██║ ╚════██║",
|
||||||
|
" ███████║ ██║ ███████║ ██║ ██║ ██║ ██║ ███████║",
|
||||||
|
" ╚══════╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝",
|
||||||
|
]
|
||||||
|
|
||||||
|
BOOT_LINES = [
|
||||||
|
("> SYSTATS v{ver}", 0.0),
|
||||||
|
("> initializing system probes... OK", 0.3),
|
||||||
|
("> scanning CPU cores... {cores} detected", 0.6),
|
||||||
|
("> GPU subsystem... {gpu}", 0.9),
|
||||||
|
("> network interfaces... ONLINE", 1.1),
|
||||||
|
("> thermal sensors... {temps}", 1.3),
|
||||||
|
("> process table... READY", 1.5),
|
||||||
|
("> Electric Entropy Lab // SYSTATS", 1.8),
|
||||||
|
]
|
||||||
|
|
||||||
|
import random as _random
|
||||||
|
|
||||||
|
|
||||||
|
def intro_splash(stdscr):
|
||||||
|
"""Animated boot splash inspired by Spectre/Pulse."""
|
||||||
|
curses.curs_set(0)
|
||||||
|
stdscr.nodelay(False)
|
||||||
|
stdscr.timeout(30)
|
||||||
|
init_colors()
|
||||||
|
|
||||||
|
h, w = stdscr.getmaxyx()
|
||||||
|
if h < 15 or w < 60:
|
||||||
|
return # skip intro on tiny terminals
|
||||||
|
|
||||||
|
# Gather system info for boot lines
|
||||||
|
cores = psutil.cpu_count()
|
||||||
|
gpu_str = "NVIDIA" if NVIDIA_AVAILABLE else ("AMD" if os.path.exists(AMD_GPU_PATH) else "NONE")
|
||||||
|
try:
|
||||||
|
temps_count = sum(len(v) for v in psutil.sensors_temperatures().values())
|
||||||
|
temps_str = f"{temps_count} sensors"
|
||||||
|
except Exception:
|
||||||
|
temps_str = "N/A"
|
||||||
|
|
||||||
|
glitch_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@#$%&*!?<>/\\|{}[]~^"
|
||||||
|
|
||||||
|
# Phase 1: Boot lines
|
||||||
|
boot_start = time.time()
|
||||||
|
shown_lines = []
|
||||||
|
|
||||||
|
for line_tpl, delay in BOOT_LINES:
|
||||||
|
line = line_tpl.format(ver=VERSION, cores=cores, gpu=gpu_str, temps=temps_str)
|
||||||
|
target_time = boot_start + delay
|
||||||
|
while time.time() < target_time:
|
||||||
|
stdscr.getch() # drain input
|
||||||
|
time.sleep(0.02)
|
||||||
|
|
||||||
|
shown_lines.append(line)
|
||||||
|
stdscr.erase()
|
||||||
|
start_y = (h - len(BOOT_LINES) - len(LOGO_ART) - 4) // 2
|
||||||
|
for i, sl in enumerate(shown_lines):
|
||||||
|
col = C_LOW if "OK" in sl or "READY" in sl or "ONLINE" in sl or "detected" in sl else C_DIM
|
||||||
|
if "Electric Entropy" in sl:
|
||||||
|
col = C_ACCENT
|
||||||
|
safe_addstr(stdscr, start_y + i, (w - len(sl)) // 2, sl,
|
||||||
|
curses.color_pair(col))
|
||||||
|
stdscr.refresh()
|
||||||
|
|
||||||
|
time.sleep(0.3)
|
||||||
|
|
||||||
|
# Phase 2: Decrypt logo animation
|
||||||
|
logo_start_y = (h - len(LOGO_ART)) // 2
|
||||||
|
# Find max logo width
|
||||||
|
max_logo_w = max(len(l) for l in LOGO_ART)
|
||||||
|
|
||||||
|
# Initialize with random chars for non-space positions
|
||||||
|
logo_state = []
|
||||||
|
for line in LOGO_ART:
|
||||||
|
row = []
|
||||||
|
for ch in line:
|
||||||
|
if ch == ' ' or ch == '\u2500':
|
||||||
|
row.append(ch)
|
||||||
|
else:
|
||||||
|
row.append(_random.choice(glitch_chars))
|
||||||
|
logo_state.append(row)
|
||||||
|
|
||||||
|
# Decrypt columns left to right
|
||||||
|
total_cols = max_logo_w
|
||||||
|
decrypt_steps = 20 # frames for full decrypt
|
||||||
|
chars_per_step = max(1, total_cols // decrypt_steps)
|
||||||
|
|
||||||
|
revealed = [set() for _ in LOGO_ART]
|
||||||
|
|
||||||
|
for step in range(decrypt_steps + 5):
|
||||||
|
stdscr.erase()
|
||||||
|
|
||||||
|
# Reveal columns up to this step
|
||||||
|
reveal_up_to = min(step * chars_per_step, total_cols)
|
||||||
|
|
||||||
|
for row_idx, line in enumerate(LOGO_ART):
|
||||||
|
lx = (w - max_logo_w) // 2
|
||||||
|
for col_idx, ch in enumerate(line):
|
||||||
|
if col_idx < reveal_up_to:
|
||||||
|
revealed[row_idx].add(col_idx)
|
||||||
|
|
||||||
|
display = []
|
||||||
|
for col_idx in range(len(line)):
|
||||||
|
if col_idx in revealed[row_idx]:
|
||||||
|
display.append(line[col_idx])
|
||||||
|
elif line[col_idx] == ' ':
|
||||||
|
display.append(' ')
|
||||||
|
else:
|
||||||
|
display.append(_random.choice(glitch_chars))
|
||||||
|
|
||||||
|
text = "".join(display)
|
||||||
|
# Color: revealed chars in cyan, glitch in dim
|
||||||
|
for col_idx, ch in enumerate(text):
|
||||||
|
if col_idx >= w - lx - 1:
|
||||||
|
break
|
||||||
|
if ch == ' ':
|
||||||
|
continue
|
||||||
|
if col_idx in revealed[row_idx]:
|
||||||
|
attr = curses.color_pair(C_TITLE) | curses.A_BOLD
|
||||||
|
else:
|
||||||
|
attr = curses.color_pair(C_DIM) | curses.A_DIM
|
||||||
|
try:
|
||||||
|
stdscr.addch(logo_start_y + row_idx, lx + col_idx, ch, attr)
|
||||||
|
except curses.error:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Version + brand below logo
|
||||||
|
if step > decrypt_steps - 3:
|
||||||
|
ver_str = f"v{VERSION} // Electric Entropy Lab"
|
||||||
|
safe_addstr(stdscr, logo_start_y + len(LOGO_ART) + 1, (w - len(ver_str)) // 2,
|
||||||
|
ver_str, curses.color_pair(C_ACCENT) | curses.A_BOLD)
|
||||||
|
|
||||||
|
stdscr.refresh()
|
||||||
|
stdscr.getch() # drain
|
||||||
|
time.sleep(0.05)
|
||||||
|
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
# Phase 3: Flash and clear
|
||||||
|
stdscr.erase()
|
||||||
|
stdscr.refresh()
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
# ─── Main ───────────────────────────────────────────────────────────
|
# ─── Main ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
show_intro = True
|
||||||
|
|
||||||
|
|
||||||
def main(stdscr):
|
def main(stdscr):
|
||||||
|
if show_intro:
|
||||||
|
intro_splash(stdscr)
|
||||||
|
|
||||||
curses.curs_set(0)
|
curses.curs_set(0)
|
||||||
stdscr.nodelay(True)
|
stdscr.nodelay(True)
|
||||||
stdscr.timeout(int(REFRESH_INTERVAL * 1000))
|
stdscr.timeout(int(REFRESH_INTERVAL * 1000))
|
||||||
@@ -1430,6 +1587,8 @@ if __name__ == "__main__":
|
|||||||
help=f"UI language: {lang_help}")
|
help=f"UI language: {lang_help}")
|
||||||
parser.add_argument("--reset-lang", action="store_true",
|
parser.add_argument("--reset-lang", action="store_true",
|
||||||
help="Reset saved language preference and ask again")
|
help="Reset saved language preference and ask again")
|
||||||
|
parser.add_argument("--no-intro", action="store_true",
|
||||||
|
help="Skip the boot intro animation")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.reset_lang:
|
if args.reset_lang:
|
||||||
@@ -1458,6 +1617,9 @@ if __name__ == "__main__":
|
|||||||
save_lang(lang)
|
save_lang(lang)
|
||||||
current_lang = lang
|
current_lang = lang
|
||||||
|
|
||||||
|
if args.no_intro:
|
||||||
|
show_intro = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
curses.wrapper(main)
|
curses.wrapper(main)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
|||||||
Reference in New Issue
Block a user