Dog loader: halve size (32x22) and drop the paw trail

This commit is contained in:
2026-09-10 14:23:08 +03:00
parent e8b1a2e83f
commit d1384e8603
5 changed files with 73 additions and 163 deletions
+1 -1
View File
@@ -10,7 +10,7 @@
- Экран **Dashboard**: полноэкранный индикатор загрузки — анимированная
**собачка из GIF** (12 кадров, 40 мс), отрисованная truecolor
half-block'ами (▀) с прозрачным фоном поверх затемнённого дашборда
(`$background 80%`), под ней — бегущая дорожка из 🐾.
(`$background 80%`).
- На время загрузки ввод блокируется (горячие клавиши и фокус), после
завершения — возвращается.
- Показывается при автообновлении по таймеру и при ручном обновлении (`r`);
+1 -1
View File
@@ -12,7 +12,7 @@ TUI-приложение (Python 3.10+) для отображения потре
- Детализация метрик конкретной модели (Enter в таблице моделей)
- Офлайн-режим: при недоступности API показывает последние данные из кэша
- Автообновление по интервалу из конфигурации (по умолчанию 60 с)
- Полноэкранный индикатор загрузки: анимированная собачка (truecolor) с 🐾
- Полноэкранный индикатор загрузки: анимированная собачка (truecolor)
- Экспорт истории в CSV/JSON, диагностика окружения (`doctor`)
## Интерфейс
+2 -14
View File
@@ -59,12 +59,11 @@ class DogLoader(Static):
"""Полноэкранный индикатор загрузки: анимация собачки из GIF.
Рисуется truecolor half-block'ами (2 пикселя на знакоместо), фон
прозрачный — просвечивает затемнённый дашборд. Под собачкой бежит
дорожка из 🐾. Показывается на время обновления и блокирует ввод.
прозрачный — просвечивает затемнённый дашборд. Показывается на время
обновления и блокирует ввод.
"""
INTERVAL = dog_art.FRAME_MS / 1000
PAW_SLOTS = 9
frame = reactive(0)
@@ -101,20 +100,11 @@ class DogLoader(Static):
self._frames = dog_art.decode_frames()
return self._frames
def _paws(self, index: int, width: int) -> str:
cells = "".join(
"🐾 " if (i + index) % 2 == 0 else " "
for i in range(self.PAW_SLOTS)
).rstrip()
pad = max(0, (width - len(cells)) // 2)
return " " * pad + cells
def render(self) -> Any:
frames = self._get_frames()
data = frames[self.frame % len(frames)]
width, px_height = dog_art.WIDTH, dog_art.PX_HEIGHT
palette = dog_art.PALETTE
theme = self.app.current_theme
text = Text(justify="center", no_wrap=True)
for y in range(0, px_height, 2):
@@ -138,8 +128,6 @@ class DogLoader(Static):
)
if y + 2 < px_height:
text.append("\n")
text.append("\n\n")
text.append(self._paws(self.frame, width), style=str(theme.secondary))
return text
@on(InputEvent)
+66 -145
View File
@@ -1,7 +1,7 @@
"""Кадры анимации индикатора-собачки (авто-сгенерировано, не редактировать).
Источник: 236205.gif; квантование в палитру, фон прозрачный (индекс 0).
Кадр хранится как 64x42 индексов палитры (по строке).
Кадр хранится как 32x22 индексов палитры (по строке).
"""
from __future__ import annotations
@@ -9,162 +9,83 @@ from __future__ import annotations
import base64
import zlib
WIDTH = 64
PX_HEIGHT = 42
WIDTH = 32
PX_HEIGHT = 22
FRAME_COUNT = 12
FRAME_MS = 40
# Индекс 0 — прозрачный фон; остальные — RGB кадров GIF.
PALETTE: tuple[tuple[int, int, int], ...] = (
(0,0,0), (255,255,255), (255,243,255), (255,255,238), (255,255,227), (255,255,221),
(255,255,207), (255,250,188), (250,255,245), (250,255,226), (249,251,237),
(246,248,245), (250,251,216), (248,248,214), (244,244,216), (246,242,193),
(239,249,234), (238,241,219), (236,237,211), (237,236,201), (231,239,224),
(183,246,251), (238,228,215), (246,219,178), (253,205,185), (253,202,176),
(250,197,180), (249,195,173), (240,190,172), (237,185,168), (224,229,216),
(222,221,200), (220,206,188), (221,184,175), (204,207,201), (198,189,183),
(185,187,181), (164,194,224), (213,166,158), (189,157,150), (163,168,178),
(155,155,159), (183,136,134), (142,136,134), (231,93,136), (159,104,113),
(127,137,174), (122,115,122), (98,125,184), (103,106,127), (104,98,108),
(98,89,96), (83,92,132), (81,82,104), (233,74,126), (205,57,101), (145,65,86),
(145,41,56), (138,8,35), (130,6,27), (124,7,27), (123,0,17), (85,73,86),
(74,70,87), (69,71,98), (68,62,77), (82,50,69), (85,28,47), (111,6,22), (62,68,99),
(61,61,85), (59,57,79), (61,57,72), (57,53,72), (58,52,68), (57,51,66), (58,50,65),
(56,50,65), (55,50,66), (55,49,65), (55,49,64), (57,48,64), (55,48,64), (58,44,60),
(54,51,69), (54,49,67), (54,49,65), (53,49,65), (54,49,64), (54,48,65), (54,48,64),
(53,48,65), (53,48,64), (54,48,63), (53,48,63), (54,47,64), (53,47,64), (54,47,63),
(53,47,63), (53,47,62), (53,46,62), (53,45,60), (48,51,75), (51,49,65), (52,48,64),
(52,47,64), (50,48,64), (52,48,63), (52,47,63), (52,46,63), (51,47,63), (49,47,63),
(52,47,62), (52,46,61), (51,46,62), (48,46,61), (51,45,62), (51,45,59), (46,44,61),
(49,43,59), (47,42,59), (44,42,59), (41,37,70), (47,40,55), (42,38,55), (41,36,54),
(39,33,52), (28,23,41),
(0,0,0), (255,255,255), (255,253,255), (255,255,249), (255,255,237), (255,250,241),
(255,255,227), (255,255,205), (251,254,245), (250,252,222), (245,253,237),
(161,253,251), (248,243,233), (247,241,202), (238,240,213), (224,235,231),
(254,218,208), (253,213,181), (227,225,205), (215,222,212), (216,215,204),
(253,204,185), (248,196,180), (225,199,179), (207,202,184), (236,183,173),
(213,181,176), (216,169,159), (213,153,148), (195,218,240), (196,198,188),
(191,193,179), (186,184,180), (192,170,157), (175,187,192), (171,169,157),
(158,172,207), (102,188,250), (190,155,145), (156,155,159), (183,137,135),
(161,135,133), (142,146,165), (137,132,135), (121,138,193), (121,126,151),
(227,104,135), (187,107,111), (223,77,125), (134,118,124), (165,84,91),
(111,116,159), (119,118,123), (117,114,115), (105,105,126), (106,102,104),
(106,91,101), (91,107,177), (88,94,138), (93,92,107), (84,83,114), (87,82,88),
(83,75,91), (75,76,104), (76,70,84), (68,68,94), (69,65,77), (176,44,87),
(135,20,39), (109,27,51), (109,0,13), (69,59,76), (64,59,78), (62,58,79),
(73,43,62), (86,14,32), (59,57,81), (58,54,74), (56,53,74), (59,54,69), (57,53,68),
(57,52,69), (58,51,66), (57,51,67), (56,51,68), (56,50,66), (57,45,61),
(48,59,108), (51,52,69), (53,51,68), (53,50,71), (53,50,65), (55,49,65),
(55,49,64), (50,49,65), (54,48,64), (54,48,63), (53,48,64), (53,48,63), (51,48,65),
(54,47,63), (53,47,65), (53,47,63), (53,47,62), (52,47,65), (52,47,63), (52,47,62),
(48,47,64), (53,46,62), (52,46,62), (51,46,62), (51,45,61), (50,45,61), (51,43,60),
(48,44,63), (47,43,60), (47,43,59), (46,42,58), (44,40,63), (44,39,57), (44,38,56),
(28,24,119), (41,36,54), (41,34,53), (42,32,51), (38,31,51), (36,31,50),
(33,26,47),
)
_DATA_B64 = (
"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"
"PwncIAw="
"eNrFmftv2koahqNPSYrb4AAFN8QhQJ1wqWkJBAohMQGSkBsXqcJN6MqV5lcipJ3OElPLMv/6junlBGxPdo9UHYtI"
"KK/HjMeP33nnm5WVlRUA++N9wBsQdhn6RioWzO576wGxEMkcMdoL2Yh46v3z6VZV2FEPPWVioEafdD1uAUpyizyS"
"seraQwAoNxpEQxjV3fTaLrdbNQkZ39+cb+d89HQ6WE/bczy3fVG/fLhpZ2S91+uhXrO79/SEje38h4P8bTsjmQiP"
"MMbo1LfQg/wWn7lt70gGoscYjwexxR76/EqxuC3pSLMP2tHFoYJVfzqbPLZ007DoYYzwYG/5Hv0Tc1BPxyUpIVUR"
"wV8XhwL4NflLzr61cEIqW61eFRyD9OPL96Pvd40QcCxcwA/Axokl/q3jWX5XxG2WLsYiGQa/EC1HUix+g1lBPPFu"
"Hq5UI9GrnJe+q85wVcE9T35LLWLoeLDvpSv1iTe/b9fijekDwQ+f29E9+MEvPH32Nr+XN1TOy4+D4T0aXC3yy4Xy"
"BwcHVE4YFF5i87vQUyhuB3Yu2vk4IcimG6sO/EqV81R4RFQbcKrXlnQuW/DH+/rUtPmdjkYdWKYPSo8TrTDnV0Fk"
"+QIrsFZ+7L2lp71OSBXrCqeX+V3lj+Anv4/yashBCyzzy6btDxz7z/DN5De6CdEYQ4/IEeE9Qw9mRaHK0JOFaETx"
"7CFUrGk2c+nJb7reR7pBBjEvfsvHuoZGyPUN2gSQ1C7Wxg+4LcSo/9LP3pNfyu1yfOJW1/993z6Tvg2GQ3XYecov"
"APdSPDsr3t+ex7sTG1Cs1RYeN6Tywodi+3yrbmiUTuq/S29qAGCrfR6qmAOk2fxqjqkKMju8ZCEbX4tONMtvOnBv"
"kpst/Uqi/Kav0ODQOQcdddBjAoBPSK0Zfu90zJD2JTfn966SDvscOr+e3v9F75oLvU/+BX8E32f8+TmD3gaBBXgk"
"tRbMMpoLqWywsM/gP5Vi6BBWHjORSsdTb1hYbWhe/K6kS1UdPY5RzVX9TuOLYueLh1uBwgwxgKf8HcnrIfnzRMcU"
"0OS9OhwOvgy7T34JQhzPn13W72/aScWY87vIF/hCNF8cXLR3ZGsuL/srfBB9YSqnrcHcf0f41GkvZyn+E9YpvibG"
"A+dM5c+8lqZ6mfIrE80ZtMCvnB4bV68BEvLYcg4kDQxHQy1H2+1dyok9N0AkMu8VvPC5kUJT1ntg0fuHqX6WX+qR"
"zAARi4isgBwtRFI5Bv/bGVYAgdJ9Npj15leZ6f1qa+jz0CXqvzRgaDUv/25UHpHNb2QeHyD2dP7ffw/hMiY0U96c"
"J78gmn+H6OlM8CpO+b24uaT5IlUx7fwwWcwH9NFmaL6g8Tn9g1+y1BHI5ENC0Y4fCM0NGjcd9gnnRb4xJXb8xcRl"
"peDPhkPqVJHSUllH1y7+qHTliU4NOC5bpOaC39bdnXZNGfioSI2YC30+UHsx+0Xwu4MCIN+x+f3Ttsy8/gZQC2YZ"
"7OaGwAoYUTkiMvgNCFlhp+bdPNvIBDJ3nvLxzGxkla9eHZQq9f/olq4devFbrdB8gb9ebIDTK169gy3lM8Xy6+d2"
"stk5rdVyncFTvim/oQvymfKbKZn2/K8tGj1w3M6c37xkjub+u7x+y+QD0bN2Ma5jOz2MsXbiym/LGBnGlFD+T5wB"
"TeS5iqFWqkqf4F7MSc9+fUu2WjzPy2bXzV/Drbt3NsBymjTdxmjVD7Fxh/LLSTEPPCH8cQXWgTHJgxdlfzxyAPsF"
"onCDwNKjuYi4x/Bftj/DtpwKehc4oERmhWi1413ewJqu4cErD12Wjw2NLqxODtxHPa3RC9jrO3Fv077JvadPMJfg"
"QmnzUX/Q2kWJ9LrNk8Pe9cKT57ionY/bZ3E0wXhE+Y4t/oCYj344uD3bak1tekej4eJIft+x8/FZuG5qWNMwcuZf"
"ym+R31KJRizDXv+9cykZrHHhPjXoskYGroWcZh1Ken8jMUPvwLXoIH+HLtnlC+4243tBW+Xk1XUfKz8A/BPp+H/I"
"F8mVtRQjP6yJmwIrH9j5mFGgg0AqycrHkvKwE6x6+ncam+RS0b541i8KLUJ0MtzzGPlSozGx88WF2xjUElxcMR/I"
"PB/3anYF4yS2sly/aNxQeZsu4NT7TrO76M/8lp2PqVwxkR1UcHOpvJB/w6Xo8q9s52NMxqTnrH6J5yFppupTu8JB"
"hs78G4TV0JXeUAZTjIhzIUxv7L2kzOTVULxPBq4D+V6qzOwKRoV4DBMnpTmKyeprb/tdtXVgRwD4R/gNwHaAoQsi"
"BNOs+JFNRQrA4lcUqt78JpTLqFA/8vTfqUEaCvKuvxWuyKNBPP25rDQmtvFdgHv9OFH/xe/pvHIRW6wfhzjx4nLO"
"7/F00GvWDruHS9kzf3CQp3LZsmcBVbt28Mtn2udvSpaGxnb97dRJRuY8lJ5pE4zoFXDHyafoC/UnuEUDNCW848bW"
"RxMluHBcpi7+0W0UTlrxNds+ZavpwS/3w2LD4J6CAX6HIZYLP1dK+Jv1iXm3vOMBQIRdXws8V1+LfmTZuywIdfDO"
"v9Y4lep7bZBA4RIRXSfe/Mpzf0Wu+TgGUFC7lJqHz67+exjmQ7KdH/7VPpPuT6j/ruQ2l/Y/ds6KRdw+2+pO0HDQ"
"Oe01F8c2Q/MDzb92frDtd9l/D+iyMtsuhluGSjtJAb529kLMrJctbFH/pQ79xaX+ALuq0So3+oaJRh2X24gNZ43w"
"Kh8Klw3iOg7hbHgOXlipg1sQWP1prPR+qfZy0wfuBQi75cv5H9OH/z+Gn40H+4EUs/ywF2CWJ4SCINaY/ApR1v7c"
"VSMq9r0uADKd/C/pvMfan6OH9/5cq0Wob927+2uc36qa5Ef97DC2vL1hP6+X4kW9/nDbFsvT3rDTPLleeFNg7ef+"
"nChbiNAVHBqeLNXPVrls+3xbntvvGLlsNMJOMUzzAbYXhyOsvnT0Muinyzd9YliWSbDLQIC/bJJyaCshlQcjlHMr"
"QLQ6cz53ldnVW0dWBe7XvH9UTvrnqyHf4gm/v8ILW9j8Swc32Gh7H3tJtfiKsPlcW2FO/5DyRVj7xxtiIcgqjwUo"
"n0nW/lu5EBG7nga+q860QhV3PPn82CeGPvbcvyhX6PyPPOq/R5sgtezYSddnAZdRitH8mry9//TwqZ1PP141m6e1"
"wwWDhHV7/y2P28X5BpuGVHWRX8gnA5kL6q/9ie2fY6zGlh8V5TcT7usDbb5/sbw/R5EIvuErpmaZpmmMEXIECL/4"
"5tgiqlyqVKoachaIYZ2Lf1PiHMfz4YL5bVpapgJ+H7EHJZ6ew+ox0QP34vfJ7rXgv2QfbHqHBB/VXwIbYID/Ao3r"
"Sw0="
)
def decode_frames() -> list[bytes]:
"""Вернуть список кадров: каждый — 64*42 байт индексов палитры."""
"""Вернуть список кадров: каждый — 32*22 байт индексов палитры."""
raw = zlib.decompress(base64.b64decode(_DATA_B64))
size = WIDTH * PX_HEIGHT
return [raw[i * size:(i + 1) * size] for i in range(FRAME_COUNT)]
+3 -2
View File
@@ -396,8 +396,6 @@ async def test_dog_loader_animates(tmp_path) -> None:
await pilot.pause(0.3)
assert loader.frame != first_frame
assert str(loader.render()) != first
# дорожка из лапок присутствует
assert "🐾" in str(loader.render())
loader.stop()
assert not loader.running
@@ -407,6 +405,9 @@ def test_dog_art_frames() -> None:
assert len(frames) == dog_art.FRAME_COUNT
size = dog_art.WIDTH * dog_art.PX_HEIGHT
assert all(len(f) == size for f in frames)
# уменьшенный размер и чётная высота для half-block
assert (dog_art.WIDTH, dog_art.PX_HEIGHT) == (32, 22)
assert dog_art.PX_HEIGHT % 2 == 0
# индексы палитры в допустимом диапазоне, фон = 0
assert all(b < len(dog_art.PALETTE) for f in frames for b in f)
assert dog_art.FRAME_MS == 40