Terminal: Das prompt, dprompt and dbanner (0.9.1)
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>
This commit is contained in:
das
2026-10-21 09:17:10 +03:00
co-authored by Claude Opus 5.5
parent 66ea614e0d
commit a2bffc7fe9
72 changed files with 5506 additions and 23 deletions
@@ -0,0 +1,132 @@
"""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())