build / build (push) Successful in 38m26s
- das-shell 0.1.0: the Das two-line bash prompt by default (replaces the PS1 of Void's skel .bashrc once), dprompt (Textual prompt builder with presets and live preview) and dbanner (SSH login banner editor: canvas tools, fields with values, widgets, das-banner service for cached ones, Das OS logo and tryzub pictures). Python tests in make test. - das-server 0.3.0 depends on das-shell; the server installer mentions dprompt and dbanner; the desktop image includes it. - run-qemu.sh forwards the guest's SSH to localhost:2222. - Hardware fixes: Ventoy menu icon, boot menu titles "Das OS <version>". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
133 lines
5.3 KiB
Python
133 lines
5.3 KiB
Python
"""dprompt: builds the bash prompt. Without arguments it opens the editor."""
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
from . import ETC_DIR, config_dir, prompt
|
|
from .cli import die, need_root, resolve, run_tui, write_file
|
|
from .i18n import t
|
|
|
|
PROG = "dprompt"
|
|
|
|
|
|
def targets(system):
|
|
"""(prompt.sh, design json) of the user or of the whole system."""
|
|
base = ETC_DIR if system else config_dir()
|
|
return os.path.join(base, "prompt.sh"), os.path.join(base, "prompt.json")
|
|
|
|
|
|
def current():
|
|
"""The design in effect for this user: their own, the system one or the stock one."""
|
|
for system in (False, True):
|
|
path = targets(system)[1]
|
|
if os.path.exists(path):
|
|
try:
|
|
return prompt.load_file(path), path
|
|
except (OSError, ValueError):
|
|
continue
|
|
return prompt.load_preset("das"), None
|
|
|
|
|
|
def apply(design, system):
|
|
sh, js = targets(system)
|
|
design = prompt.normalize(design)
|
|
write_file(js, prompt.dumps(design))
|
|
write_file(sh, prompt.script(design))
|
|
return sh
|
|
|
|
|
|
def reset(system):
|
|
for path in targets(system):
|
|
try:
|
|
os.remove(path)
|
|
except FileNotFoundError:
|
|
pass
|
|
|
|
|
|
USAGE = t("""Використання: dprompt [команда]
|
|
(без команди) редактор запрошення з переглядом
|
|
list готові запрошення
|
|
show [НАЗВА|ФАЙЛ] як виглядає запрошення (типово — поточне)
|
|
--script код для bash замість вигляду
|
|
apply НАЗВА|ФАЙЛ застосувати для себе (~/.config/das/prompt.sh)
|
|
--system для всіх користувачів (/etc/das/prompt.sh, через sudo)
|
|
reset [--system] повернути стандартне запрошення
|
|
Нове запрошення діє в нових вікнах терміналу або після exec bash.""",
|
|
"""Usage: dprompt [command]
|
|
(no command) prompt editor with a live preview
|
|
list ready-made prompts
|
|
show [NAME|FILE] what the prompt looks like (default: the current one)
|
|
--script the bash code instead
|
|
apply NAME|FILE use it yourself (~/.config/das/prompt.sh)
|
|
--system for every user (/etc/das/prompt.sh, through sudo)
|
|
reset [--system] back to the stock prompt
|
|
A new prompt works in new terminal windows or after exec bash.""")
|
|
|
|
|
|
def main(argv=None):
|
|
argv = sys.argv[1:] if argv is None else argv
|
|
if not argv:
|
|
from . import prompt_app
|
|
return run_tui(PROG, prompt_app.PromptApp)
|
|
cmd, args = argv[0], argv[1:]
|
|
system = "--system" in args
|
|
args = [a for a in args if a != "--system"]
|
|
try:
|
|
if cmd in ("-h", "--help", "help"):
|
|
print(USAGE)
|
|
elif cmd == "list":
|
|
for name, title in prompt.list_presets():
|
|
print(f"{name:<12} {title}")
|
|
elif cmd == "show":
|
|
script = "--script" in args
|
|
args = [a for a in args if a != "--script"]
|
|
design = prompt.load(args[0]) if args else current()[0]
|
|
if script:
|
|
sys.stdout.write(prompt.script(design))
|
|
else:
|
|
for state in (prompt.sample_state(), prompt.sample_state(rc=1, git="main*", venv=".venv"),
|
|
prompt.sample_state(root=True)):
|
|
print(_ansi(design, state))
|
|
print()
|
|
elif cmd == "apply":
|
|
if not args:
|
|
die(PROG, "вкажіть назву або файл", "name a preset or a file")
|
|
spec = resolve(args[0])
|
|
if system:
|
|
need_root(["apply", spec, "--system"])
|
|
path = apply(prompt.load(spec), system)
|
|
print(t(f"Запрошення записано в {path}. Відкрийте новий термінал або виконайте: exec bash",
|
|
f"The prompt is written to {path}. Open a new terminal or run: exec bash"))
|
|
elif cmd == "reset":
|
|
if system:
|
|
need_root(["reset", "--system"])
|
|
reset(system)
|
|
print(t("Повернуто стандартне запрошення (діє в новому терміналі).",
|
|
"The stock prompt is back (in new terminals)."))
|
|
else:
|
|
die(PROG, f"невідома команда: {cmd}\n{USAGE}", f"unknown command: {cmd}\n{USAGE}", 2)
|
|
except FileNotFoundError as e:
|
|
die(PROG, f"не знайдено: {e.filename or e}", f"not found: {e.filename or e}")
|
|
except (ValueError, json.JSONDecodeError) as e:
|
|
die(PROG, f"помилка у файлі: {e}", f"bad file: {e}")
|
|
except PermissionError as e:
|
|
die(PROG, f"немає доступу: {e.filename}", f"permission denied: {e.filename}")
|
|
return 0
|
|
|
|
|
|
def _ansi(design, state):
|
|
from . import colors
|
|
out = []
|
|
for text, style in prompt.preview(design, state):
|
|
if style:
|
|
words = style.split()
|
|
out.append(colors.sgr(words[-1] if words[-1] != "default" else None, None, "bold" in words))
|
|
else:
|
|
out.append(colors.RESET)
|
|
out.append(text)
|
|
return "".join(out) + colors.RESET
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|