Интерфейс панели был полностью перерисован

Новый дизайн в стиле коммерческих продуктов, рассчитанный на обычного
пользователя:
- светлая и тёмная темы (следует системной), шрифт Inter и иконки
  Lucide вшиты в панель — работает без интернета;
- общий каркас base.html и компоненты _ui.html вместо 14 независимых
  страниц; адаптивная вёрстка для телефона;
- простой язык: «Работает / Выключена», «Автозапуск», «Доступ из
  интернета» вместо running, autostart, DNAT; технические данные
  убраны во вкладку «Для специалистов»;
- главная: загрузка процессора, памяти и хранилища, карточки машин с
  главным действием и меню остальных, приветственный сценарий для
  первой машины;
- мастер создания машины из трёх шагов с готовыми конфигурациями;
- страница машины с вкладками, переключателем автозапуска и порядком
  загрузки кнопками;
- «Сеть и доступ»: открытие SSH, сайта, RDP и т. д. из списка,
  ручные IP машин, понятная диагностика;
- загрузка образов перетаскиванием с прогрессом и оставшимся временем;
- подтверждения опасных действий в диалоге и уведомления;
- раздел «Помощь»;
- web/presenters.py переводит вывод virsh и /proc в данные для
  страниц; tests/dev_server.py запускает панель с имитацией virsh.

Также: исправлено определение модели CPU на x86 («207» вместо
названия процессора), история версии 0.10.0 и документация обновлены
под Ubuntu 26.04 LTS, в README добавлены скриншоты.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
Этот коммит содержится в:
Claude
2026-09-25 07:01:41 +00:00
родитель c4cebd916b
Коммит 4098c270a4
41 изменённых файлов: 3160 добавлений и 2939 удалений
+37
Просмотреть файл
@@ -65,3 +65,40 @@ def test_versions_manifest_matches_version_file():
version = (root / "VERSION").read_text().strip()
manifest = json.loads((root / "updates" / "versions.json").read_text())
assert version in [item["version"] for item in manifest["versions"]]
def test_presenters_parse_virsh_tables():
import presenters
disks = presenters.parse_domblklist(" Type Device Target Source\n------------------------------------\n file disk vda /var/lib/virtuality/images/web01.qcow2\n file cdrom sda -\n")
assert [d["name"] for d in disks] == ["web01.qcow2", "Пусто"]
assert disks[1]["is_cdrom"]
nics = presenters.parse_domiflist(" Interface Type Source Model MAC\n---\n vnet0 network virtuality-nat virtio 52:54:00:aa:bb:cc\n")
assert nics[0]["mac"] == "52:54:00:aa:bb:cc"
info = presenters.parse_dominfo("State: shut off\nCPU(s): 4\nMax memory: 8388608 KiB\nUsed memory: 8388608 KiB\nAutostart: disable\n")
assert (info["vcpus"], info["memory_mb"], info["autostart"]) == (4, 8192, False)
assert presenters.vm_state("shut off")["label"] == "Выключена"
assert presenters.format_mb(1536) == "1.5 ГБ"
assert presenters.plural(22, "ядро", "ядра", "ядер") == "ядра"
def test_update_not_offered_when_local_is_newer(tmp_path, monkeypatch, data_dirs):
import subprocess
def git(*args, cwd):
subprocess.run(["git", *args], cwd=cwd, check=True, capture_output=True)
remote, local = tmp_path / "remote", tmp_path / "local"
remote.mkdir()
git("init", "-q", "-b", "main", cwd=remote)
git("-c", "user.name=t", "-c", "user.email=t@t", "commit", "-q", "--allow-empty", "-m", "base", cwd=remote)
git("clone", "-q", str(remote), str(local), cwd=tmp_path)
(local / "VERSION").write_text("0.10.0\n")
git("add", "VERSION", cwd=local)
git("-c", "user.name=t", "-c", "user.email=t@t", "commit", "-q", "-m", "newer local build", cwd=local)
monkeypatch.setattr(update_core, "SOURCE_DIR", local)
assert update_core.check_updates(fetch=True)["has_update"] is False
git("-c", "user.name=t", "-c", "user.email=t@t", "commit", "-q", "--allow-empty", "-m", "upstream", cwd=remote)
git("reset", "-q", "--hard", "origin/main", cwd=local)
assert update_core.check_updates(fetch=True)["has_update"] is True