[+] Wizard - Allow selecting old distro logo variant (#537)
Some checks failed
Shellcheck / check (push) Has been cancelled

This commit is contained in:
Azalea
2026-10-16 05:35:38 +00:00
parent a50d5077e4
commit 63e380ea4f
8 changed files with 258 additions and 63 deletions

View File

@@ -67,6 +67,7 @@ fn main() -> Result<()> {
&dir.join("../../neofetch"),
]).context("couldn't find neofetch")?;
println!("cargo:rerun-if-changed={}", neofetch_src.display());
println!("cargo:rerun-if-changed={}", data_dir.display());
fs::copy(&neofetch_src, o.join("neofetch"))?;
preset_codegen(&o.join("hyfetch/data/presets.json"), &o.join("presets.rs"))?;

View File

@@ -1195,49 +1195,100 @@ fn create_config(
(ascii_per_row, ascii_rows)
};
// get distro string and convert it into the enum, neofetch friendly format, so we can check for small logos with the {distro}_small neofetch naming scheme.
// get distro string and convert it into the enum, neofetch friendly format, so we can check for small and old logos with the {distro}_small / {distro}_old neofetch naming scheme.
let tmp_dst = get_distro_name(backend).or_else(|_| get_distro_name(Backend::Neofetch)).context("failed to get distro name")?;
let detected_dst = Some(distro.map_or_else(
let detected_dst = distro.map_or_else(
|| {
Distro::detect(&tmp_dst)
.map_or("".to_string(), |d| format!("{:?}", d).to_lowercase())
.map_or_else(|| tmp_dst.clone(), |d| format!("{:?}", d))
},
|d| d.to_string().to_lowercase(),
));
|d| d.to_string(),
).to_lowercase();
// in case someone specified {distro}_small already in the --distro arg
let detected_dst_small_fmt = if !detected_dst.clone().unwrap().ends_with("_small") {
format!("{}_small", detected_dst.unwrap()).to_lowercase()
let raw_dst = detected_dst.as_str();
let base_distro = raw_dst
.strip_suffix("_small")
.or_else(|| raw_dst.strip_suffix("_old"))
.unwrap_or(raw_dst);
let detected_dst_old_fmt = format!("{base_distro}_old");
let running_dst_old = if Distro::detect(&detected_dst_old_fmt).is_some() {
detected_dst_old_fmt
} else {
detected_dst.unwrap()
"".to_string()
};
let detected_dst_small_fmt = format!("{base_distro}_small");
let running_dst_sml = if Distro::detect(&detected_dst_small_fmt).is_some() {
detected_dst_small_fmt
} else {
"".to_string()
};
// Load ascii for default, old, and small logos
let default_asc = if raw_dst.ends_with("_old") || raw_dst.ends_with("_small") {
get_distro_ascii(Some(base_distro), backend)
.or_else(|_| get_distro_ascii(Some(base_distro), Backend::Neofetch))
.ok()
.and_then(|a| a.to_normalized().ok())
.unwrap_or_else(|| asc.clone())
} else {
asc.clone()
};
// load ascii
let small_asc = get_distro_ascii(Some(&running_dst_sml), backend).context("failed to get distro ascii")?;
let small_asc = small_asc.to_normalized().context("failed to normalize ascii")?;
let old_asc = if !running_dst_old.is_empty() {
get_distro_ascii(Some(&running_dst_old), backend)
.or_else(|_| get_distro_ascii(Some(&running_dst_old), Backend::Neofetch))
.ok()
.and_then(|a| a.to_normalized().ok())
} else {
None
};
let small_asc = if !running_dst_sml.is_empty() {
get_distro_ascii(Some(&running_dst_sml), backend)
.or_else(|_| get_distro_ascii(Some(&running_dst_sml), Backend::Neofetch))
.ok()
.and_then(|a| a.to_normalized().ok())
} else {
None
};
let mut asc = asc;
let mut logo_chosen: Option<String> = distro.cloned();
if small_asc.lines != asc.lines && running_dst_sml != "" {
let ds_arrangements = [
("Default", asc.clone()),
("Small", small_asc.clone())
];
let mut ds_arrangements: Vec<(&str, NormalizedAsciiArt)> = vec![
("Default", default_asc.clone()),
];
if let Some(ref o) = old_asc {
if o.lines != default_asc.lines {
ds_arrangements.push(("Old", o.clone()));
}
}
if let Some(ref s) = small_asc {
if s.lines != default_asc.lines {
ds_arrangements.push(("Small", s.clone()));
}
}
if ds_arrangements.len() > 1 {
let arrangements: IndexMap<Cow<str>, NormalizedAsciiArt> =
ds_arrangements.map(|(k, a)| (k.into(), a)).into();
ds_arrangements.into_iter().map(|(k, a)| (k.into(), a)).collect();
let has_old = arrangements.contains_key("Old");
let has_small = arrangements.contains_key("Small");
let max_h = arrangements.values().map(|a| usize::from(a.h)).max().unwrap_or(usize::from(asc.h));
let max_w = arrangements.values().map(|a| usize::from(a.w)).max().unwrap_or(usize::from(asc.w));
{
clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?;
let (Width(term_w), _) = terminal_size().unwrap_or((Width(80), Height(24)));
let ascii_per_row = (term_w / (max_w as u16 + 6)).clamp(1, u8::MAX.into()) as u8;
let asciis: Vec<Vec<String>> = arrangements
.iter()
.map(|(k, a)| {
@@ -1245,51 +1296,71 @@ fn create_config(
.to_recolored(&ColorAlignment::Horizontal, &color_profile, color_mode, theme)
.context("failed to recolor ascii")?
.lines;
if k == "Small" {
// vertical center
let pad_len = (asc.h as usize - v.len()) / 2;
let mut pad_v: Vec<String> = vec![];
for _ in 0..pad_len {
pad_v.push("".to_string());
}
v.splice(0..0, pad_v.clone());
v.extend(pad_v);
let pad_diff = asc.h as usize - v.len();
v.extend(std::iter::repeat("".to_string()).take(pad_diff));
v.push(format!("{k:^asc_width$}", asc_width = usize::from(small_asc.w)));
return Ok(v);
}
v.push(format!("{k:^asc_width$}", asc_width = usize::from(asc.w)));
Ok(v)
let pad_len = max_h.saturating_sub(v.len()) / 2;
let empty_line = " ".repeat(usize::from(a.w));
let pad_v: Vec<String> = vec![empty_line.clone(); pad_len];
v.splice(0..0, pad_v);
let pad_diff = max_h.saturating_sub(v.len());
v.extend(std::iter::repeat(empty_line).take(pad_diff));
v.push(format!("{k:^asc_width$}", asc_width = usize::from(a.w)));
Ok(v)
})
.collect::<Result<_>>()?;
// prints small logo w/ big logo
for row in &asciis.into_iter().chunks(usize::from(ascii_per_row)) {
let row: Vec<Vec<String>> = row.collect();
for i in 0..usize::from(asc.h).checked_add(1).unwrap() {
for i in 0..=max_h {
let mut line = Vec::new();
for lines in &row {
line.push(&*lines[i]);
line.push(&*lines[i]);
}
printc(line.join(" "), color_mode).context("failed to print ascii line")?;
printc(line.join(" "), color_mode).context("failed to print ascii line")?;
}
println!();
}
print_title_prompt(option_counter, "Do you want the default logo, or the small logo?");
let opts: Vec<Cow<str>> = ["default", "small"].map(Into::into).into();
let prompt_text = match (has_old, has_small) {
(true, true) => "Do you want the default logo, the old logo, or the small logo?",
(true, false) => "Do you want the default logo, or the old logo?",
(false, true) => "Do you want the default logo, or the small logo?",
(false, false) => unreachable!(),
};
print_title_prompt(option_counter, prompt_text);
let mut opts: Vec<Cow<str>> = vec!["default".into()];
if has_old {
opts.push("old".into());
}
if has_small {
opts.push("small".into());
}
let choice = literal_input("Your choice?", &opts[..], "default", true, color_mode)
.context("failed to ask for choice input")
.context("failed to select logo type").context("failed to ask for choice input")?;
.context("failed to select logo type")?;
if choice.to_lowercase() == "small" {
logo_chosen = Some(running_dst_sml);
asc = small_asc;
match choice.to_lowercase().as_str() {
"old" => {
logo_chosen = Some(running_dst_old);
if let Some(o) = old_asc {
asc = o;
}
}
"small" => {
logo_chosen = Some(running_dst_sml);
if let Some(s) = small_asc {
asc = s;
}
}
_ => {
logo_chosen = distro.map(|_| base_distro.to_string());
asc = default_asc;
}
}
update_title(

View File

@@ -142,6 +142,18 @@ where
return Some(selected.as_ref());
}
// Aliases
if sel == "original" {
if let Some(selected) = options.iter().find(|&o| o.as_ref().to_lowercase() == "old") {
return Some(selected.as_ref());
}
}
if sel == "new" || sel == "regular" {
if let Some(selected) = options.iter().find(|&o| o.as_ref().to_lowercase() == "default") {
return Some(selected.as_ref());
}
}
// Find starting abbreviation
if let Some(selected) = options
.iter()
@@ -190,6 +202,13 @@ pub fn add_pkg_path() -> Result<()> {
/// [neofetch]: https://github.com/hykilpikonna/hyfetch#running-updated-original-neofetch
pub fn neofetch_path() -> Result<PathBuf> {
if let Ok(p) = which("neowofetch") {
if let Ok(first_line) = fs::read_to_string(&p).map(|s| s.lines().next().unwrap_or("").to_owned()) {
if !first_line.contains("python") {
return Ok(p);
}
}
}
if let Ok(p) = which("neofetch") {
return Ok(p);
}

View File

@@ -1,4 +1,4 @@
{"match": "\"void_old\"", "color": "2 8"}
{"match": "\"void_old\"", "color": "2 8", "foreground": [2], "background": 1}
${c1} __.;=====;.__
_.=+==++=++=+=+===;.
-=+++=+===+=+=+++++=_
@@ -6,7 +6,7 @@ ${c1} __.;=====;.__
_vi, ` --+=++++:
.uvnvi. _._ -==+==+.
.vvnvnI` .;==|==;. :|=||=|.
${c2}+QmQQm${c1}pvvnv; ${c2}_yYsyQQWUUQQQm #QmQ#${c1}:${c2}QQQWUV$QQmL
${c2}+QmQQm${c1}pvvnv; ${c2}_yYsyQQWUUQQQm #QmQ#${c1}:${c2}QQQWUV$QQm.
${c2} -QQWQW${c1}pvvo${c2}wZ?.wQQQE${c1}==<${c2}QWWQ/QWQW.QQWW${c1}(: ${c2}jQWQE
${c2} -$QQQQmmU' jQQQ@${c1}+=<${c2}QWQQ)mQQQ.mQQQC${c1}+;${c2}jWQQ@'
${c2} -$WQ8Y${c1}nI: ${c2}QWQQwgQQWV${c1}`${c2}mWQQ.jQWQQgyyWW@!
@@ -16,4 +16,4 @@ ${c1} -1vvnvv. `~+++` ++|+++
-Invnvvnsi..___..=sv=. `
+Invnvnvnnnnnnnnvvnn;.
~|Invnvnvvnvvvnnv}+`
-~|{*l}*|
-~|{*l}*|~

View File

@@ -17,6 +17,7 @@ from . import termenv, neofetch_util, pride_month
from .color_scale import Scale
from .color_util import clear_screen
from .constants import *
from .distros import distro_detector
from .font_logo import get_font_logo
from .models import build_hex_color_profile, Config
from .neofetch_util import *
@@ -51,12 +52,12 @@ def check_config(path) -> Config:
try:
return Config.from_dict(json.loads(path.read_text('utf-8')))
except KeyError:
return create_config()
return create_config(path)
return create_config()
return create_config(path)
def create_config() -> Config:
def create_config(path: Path | None = None) -> Config:
"""
Create config interactively
@@ -432,10 +433,102 @@ def create_config() -> Config:
_prs = _prs.set_light_dl(lightness, light_dark)
update_title('Selected Brightness', f"{lightness:.2f}")
##############################
# 5. Choose Default, Old, or Small Logo
tmp_dst = GLOBAL_CFG.override_distro or get_distro_name()
detected_dst = GLOBAL_CFG.override_distro
if not detected_dst:
det = distro_detector.detect(tmp_dst)
detected_dst = det.name if det else tmp_dst
detected_dst = (detected_dst or '').lower()
raw_dst = detected_dst
base_distro = raw_dst
for suffix in ('_small', '_old'):
if base_distro.endswith(suffix):
base_distro = base_distro[:-len(suffix)]
break
running_dst_old = f"{base_distro}_old" if distro_detector.detect(f"{base_distro}_old") else ""
running_dst_sml = f"{base_distro}_small" if distro_detector.detect(f"{base_distro}_small") else ""
default_asc = get_distro_ascii(base_distro) if (raw_dst.endswith('_old') or raw_dst.endswith('_small')) else asc
old_asc = get_distro_ascii(running_dst_old) if running_dst_old else None
small_asc = get_distro_ascii(running_dst_sml) if running_dst_sml else None
logo_chosen: str | None = GLOBAL_CFG.override_distro
ds_arrangements = [
('Default', default_asc)
]
if old_asc and old_asc.strip().splitlines() != default_asc.strip().splitlines():
ds_arrangements.append(('Old', old_asc))
if small_asc and small_asc.strip().splitlines() != default_asc.strip().splitlines():
ds_arrangements.append(('Small', small_asc))
if len(ds_arrangements) > 1:
has_old = 'Old' in [k for k, _ in ds_arrangements]
has_small = 'Small' in [k for k, _ in ds_arrangements]
max_h = max(ascii_size(a)[1] for _, a in ds_arrangements)
max_w = max(ascii_size(a)[0] for _, a in ds_arrangements)
while True:
clear_screen(title)
term_w = term_size()[0]
ascii_per_row = max(1, term_w // (max_w + 6))
asciis = []
for k, a in ds_arrangements:
w, h = ascii_size(a)
v_dst = running_dst_old if k == 'Old' else (running_dst_sml if k == 'Small' else base_distro)
v_ca = ColorAlignment('horizontal', fore_back=get_fore_back(v_dst))
lines = v_ca.recolor_ascii(a, _prs).split('\n')
pad_top = (max_h - len(lines)) // 2
pad_bottom = max_h - len(lines) - pad_top
padded = [' ' * w] * pad_top + lines + [' ' * w] * pad_bottom
padded.append(k.center(w))
asciis.append(padded)
while asciis:
current = asciis[:ascii_per_row]
asciis = asciis[ascii_per_row:]
for line in zip(*current):
printc(' '.join(line))
print()
if has_old and has_small:
prompt_text = "Do you want the default logo, the old logo, or the small logo?"
opts = ['default', 'old', 'small']
elif has_old:
prompt_text = "Do you want the default logo, or the old logo?"
opts = ['default', 'old']
else:
prompt_text = "Do you want the default logo, or the small logo?"
opts = ['default', 'small']
print_title_prompt(prompt_text)
choice = literal_input('Your choice?', opts, 'default')
if choice == 'old':
logo_chosen = running_dst_old
asc = old_asc
elif choice == 'small':
logo_chosen = running_dst_sml
asc = small_asc
else:
logo_chosen = base_distro if GLOBAL_CFG.override_distro else None
asc = default_asc
asc_width, asc_lines = ascii_size(asc)
update_title('Selected logo type', choice)
break
#############################
# 5. Color arrangement
# 6. Color arrangement
color_alignment = None
fore_back = get_fore_back()
fore_back = get_fore_back(logo_chosen or base_distro)
# Calculate amount of row/column that can be displayed on screen
ascii_per_row = max(1, term_size()[0] // (asc_width + 2))
@@ -571,13 +664,13 @@ def create_config() -> Config:
# Create config
clear_screen(title)
c = Config(preset, color_system, light_dark, lightness, color_alignment, backend, custom_ascii_path=custom_ascii_path)
c = Config(preset, color_system, light_dark, lightness, color_alignment, backend, distro=logo_chosen, custom_ascii_path=custom_ascii_path)
# Save config
print()
save = literal_input(f'Save config?', ['y', 'n'], 'y')
if save == 'y':
c.save()
c.save(path)
return c
@@ -661,7 +754,7 @@ def run():
args.config_file = CONFIG_PATH
# Load config or create config
config = create_config() if args.config else check_config(args.config_file)
config = create_config(args.config_file) if args.config else check_config(args.config_file)
# Check if it's June (pride month)
now = datetime.datetime.now()
@@ -682,7 +775,7 @@ def run():
june_path.touch()
# Use a custom distro
GLOBAL_CFG.override_distro = args.distro or config.distro
GLOBAL_CFG.override_distro = (config.distro if args.config else (args.distro or config.distro))
# Param overwrite config
if args.preset:

View File

@@ -1,6 +1,7 @@
from __future__ import annotations
from dataclasses import dataclass, field
from pathlib import Path
from .constants import CONFIG_PATH
from .neofetch_util import ColorAlignment
@@ -43,9 +44,10 @@ class Config:
d['color_align'] = ColorAlignment.from_dict(d['color_align'])
return from_dict(cls, d)
def save(self):
CONFIG_PATH.parent.mkdir(exist_ok=True, parents=True)
CONFIG_PATH.write_text(json_stringify(self, indent=4), 'utf-8')
def save(self, path: Path | None = None):
target = Path(path) if path else CONFIG_PATH
target.parent.mkdir(exist_ok=True, parents=True)
target.write_text(json_stringify(self, indent=4), 'utf-8')
def custom_preset_profiles(self) -> dict[str, ColorProfile]:
profiles: dict[str, ColorProfile] = {}

View File

@@ -50,6 +50,12 @@ def literal_input(prompt: str, options: Iterable[str], default: str, show_ops: b
if sel in lows:
return options[lows.index(sel)]
# Aliases
if sel == 'original' and 'old' in lows:
return options[lows.index('old')]
if sel in ('new', 'regular') and 'default' in lows:
return options[lows.index('default')]
# Find starting abbreviation
for i, op in enumerate(lows):
if op.startswith(sel):
@@ -182,7 +188,10 @@ class ColorAlignment:
# Add new colors
if self.mode == 'horizontal':
colors = preset.with_length(len(lines))
asc = '\n'.join([l.replace(f'${{c{back}}}', colors[i].to_ansi()) + color('&~&*') for i, l in enumerate(lines)])
if back:
asc = '\n'.join([l.replace(f'${{c{back}}}', colors[i].to_ansi()) + color('&~&*') for i, l in enumerate(lines)])
else:
asc = '\n'.join([re.sub(RE_NEOFETCH_COLOR, colors[i].to_ansi(), l) + color('&~&*') for i, l in enumerate(lines)])
else:
raise NotImplementedError()

View File

@@ -16152,7 +16152,7 @@ ${c1} __.;=====;.__
_vi, ` --+=++++:
.uvnvi. _._ -==+==+.
.vvnvnI` .;==|==;. :|=||=|.
${c2}+QmQQm${c1}pvvnv; ${c2}_yYsyQQWUUQQQm #QmQ#${c1}:${c2}QQQWUV$QQmL
${c2}+QmQQm${c1}pvvnv; ${c2}_yYsyQQWUUQQQm #QmQ#${c1}:${c2}QQQWUV$QQm.
${c2} -QQWQW${c1}pvvo${c2}wZ?.wQQQE${c1}==<${c2}QWWQ/QWQW.QQWW${c1}(: ${c2}jQWQE
${c2} -$QQQQmmU' jQQQ@${c1}+=<${c2}QWQQ)mQQQ.mQQQC${c1}+;${c2}jWQQ@'
${c2} -$WQ8Y${c1}nI: ${c2}QWQQwgQQWV${c1}`${c2}mWQQ.jQWQQgyyWW@!
@@ -16162,7 +16162,7 @@ ${c1} -1vvnvv. `~+++` ++|+++
-Invnvvnsi..___..=sv=. `
+Invnvnvnnnnnnnnvvnn;.
~|Invnvnvvnvvvnnv}+`
-~|{*l}*|
-~|{*l}*|~
EOF
;;
"Void"*)