9fdb02c72a
Списки баз стенда и локальных дампов на одном экране, фильтр сужает их на каждом набранном символе. Enter по базе открывает окно параметров дампа с галочкой автоподнятия: снята — только dump, поднята — dump плюс restore. Правки полей уезжают в копию конфига, config.json не трогается. Ядро получило четыре хука для UI (лог, границы шагов, пароль, отмена), run() читает вывод утилит построчно через Popen, поэтому журнал наполняется во время работы pg_dump. Текстовый режим, CLI и --dry-run ведут себя как раньше; без textual или вне tty пульт не запускается. Журнал разворачивается на весь экран по F4 и сам на старте операции. Раскладка адаптивная: narrow ниже 100 колонок, short ниже 32 строк. Ctrl-хоткеи продублированы кириллицей — русская раскладка их не глушит. Запуск через run.ps1 (venv, UTF-8-консоль, -Setup и -Tests). Проверка — tests_tui.py: 46 тестов, TUI гоняется headless через Pilot.
312 lines
4.8 KiB
Plaintext
312 lines
4.8 KiB
Plaintext
/* Пульт pg-stand-sync. Литеральных цветов здесь нет: всё через переменные темы,
|
|
иначе светлая тема рассыпается на первом же захардкоженном #hex. */
|
|
|
|
Screen {
|
|
background: $background;
|
|
color: $foreground;
|
|
}
|
|
|
|
#topbar {
|
|
height: 2;
|
|
padding: 0 1;
|
|
background: $surface;
|
|
color: $foreground;
|
|
}
|
|
|
|
#board {
|
|
layout: horizontal;
|
|
height: 1fr;
|
|
min-height: 6;
|
|
}
|
|
|
|
#pane-db {
|
|
width: 1fr;
|
|
}
|
|
|
|
#col-right {
|
|
width: 1fr;
|
|
}
|
|
|
|
.panel {
|
|
border: round $panel;
|
|
background: $surface;
|
|
padding: 0 1;
|
|
}
|
|
|
|
.panel:focus-within {
|
|
border: round $primary;
|
|
}
|
|
|
|
.panel > Input {
|
|
border: none;
|
|
height: 1;
|
|
padding: 0;
|
|
background: $surface;
|
|
}
|
|
|
|
.panel > OptionList {
|
|
height: 1fr;
|
|
background: $surface;
|
|
border: none;
|
|
padding: 0;
|
|
scrollbar-size-vertical: 1;
|
|
}
|
|
|
|
#target-card {
|
|
height: auto;
|
|
border: round $panel;
|
|
background: $surface;
|
|
padding: 0 1;
|
|
}
|
|
|
|
#pane-dump {
|
|
height: 1fr;
|
|
}
|
|
|
|
#bottom {
|
|
height: 20;
|
|
max-height: 60%;
|
|
min-height: 6;
|
|
}
|
|
|
|
#plan {
|
|
width: 36;
|
|
border: round $panel;
|
|
background: $surface;
|
|
padding: 0 1;
|
|
}
|
|
|
|
#log {
|
|
width: 1fr;
|
|
border: round $panel;
|
|
background: $surface;
|
|
padding: 0 1;
|
|
scrollbar-size-vertical: 1;
|
|
}
|
|
|
|
#status {
|
|
height: 1;
|
|
padding: 0 1;
|
|
background: $surface;
|
|
}
|
|
|
|
#op-spin {
|
|
width: 2;
|
|
color: $accent;
|
|
}
|
|
|
|
#op-title {
|
|
width: 1fr;
|
|
content-align: left middle;
|
|
}
|
|
|
|
#op-timer {
|
|
width: 10;
|
|
content-align: right middle;
|
|
color: $text-muted;
|
|
}
|
|
|
|
#op-progress {
|
|
width: 22;
|
|
}
|
|
|
|
/* Во время операции списки заперты: менять выбор посреди restore нечего */
|
|
.locked {
|
|
opacity: 0.55;
|
|
}
|
|
|
|
.panel.locked {
|
|
border: round $panel;
|
|
}
|
|
|
|
.pane-error {
|
|
border: round $error;
|
|
}
|
|
|
|
#db-error, #dump-note {
|
|
height: auto;
|
|
padding: 0 1;
|
|
color: $text-error;
|
|
display: none;
|
|
}
|
|
|
|
#db-error.shown, #dump-note.shown {
|
|
display: block;
|
|
}
|
|
|
|
/* --------------------------------------------------------------- модалки */
|
|
|
|
ModalScreen {
|
|
align: center middle;
|
|
background: $background 60%;
|
|
}
|
|
|
|
.dialog {
|
|
width: 72;
|
|
height: auto;
|
|
max-height: 80%;
|
|
border: round $primary;
|
|
background: $surface;
|
|
padding: 1 2;
|
|
opacity: 1;
|
|
transition: opacity 140ms;
|
|
}
|
|
|
|
.dialog > .title {
|
|
text-style: bold;
|
|
color: $text-primary;
|
|
padding-bottom: 1;
|
|
}
|
|
|
|
.dialog > .subject {
|
|
padding-bottom: 1;
|
|
}
|
|
|
|
.dialog Input {
|
|
margin-bottom: 1;
|
|
}
|
|
|
|
.danger {
|
|
background: $error 25%;
|
|
color: $text-error;
|
|
border-left: thick $error;
|
|
padding: 0 1;
|
|
margin-bottom: 1;
|
|
height: auto;
|
|
}
|
|
|
|
.danger.hidden {
|
|
display: none;
|
|
}
|
|
|
|
.row {
|
|
height: auto;
|
|
align-horizontal: right;
|
|
}
|
|
|
|
.row > Button {
|
|
margin-left: 2;
|
|
}
|
|
|
|
.switch-row {
|
|
height: auto;
|
|
margin-bottom: 1;
|
|
}
|
|
|
|
.switch-row > Label {
|
|
padding: 1 1 0 1;
|
|
}
|
|
|
|
#help-body {
|
|
height: auto;
|
|
}
|
|
|
|
/* Узкое окно: пульт складывается в колонку, обзорность теряется, но ничего не режется */
|
|
.narrow #board {
|
|
layout: vertical;
|
|
height: 1fr;
|
|
}
|
|
|
|
.narrow #pane-db {
|
|
width: 1fr;
|
|
height: 1fr;
|
|
min-height: 5;
|
|
}
|
|
|
|
.narrow #col-right {
|
|
width: 1fr;
|
|
height: 12;
|
|
}
|
|
|
|
.narrow.short #col-right {
|
|
height: 7;
|
|
}
|
|
|
|
.narrow #bottom {
|
|
layout: vertical;
|
|
}
|
|
|
|
.narrow #plan {
|
|
width: 1fr;
|
|
height: 35%;
|
|
min-height: 3;
|
|
}
|
|
|
|
.dialog > .form {
|
|
height: auto;
|
|
max-height: 24;
|
|
}
|
|
|
|
.dialog .section {
|
|
text-style: bold;
|
|
color: $text-primary;
|
|
padding-top: 1;
|
|
}
|
|
|
|
.dialog Collapsible {
|
|
border: none;
|
|
padding: 0;
|
|
margin-bottom: 1;
|
|
}
|
|
|
|
.dialog #target-block {
|
|
height: auto;
|
|
}
|
|
|
|
.dialog #target-block.hidden {
|
|
display: none;
|
|
}
|
|
|
|
.dialog .form Label {
|
|
color: $text-muted;
|
|
padding: 0 0 0 1;
|
|
}
|
|
|
|
.dialog .form Input {
|
|
width: 1fr;
|
|
border: tall $panel;
|
|
background: $boost;
|
|
}
|
|
|
|
.dialog .form Input:focus {
|
|
border: tall $primary;
|
|
}
|
|
|
|
.dialog Collapsible > CollapsibleTitle {
|
|
color: $text-primary;
|
|
}
|
|
|
|
.row > Button {
|
|
min-width: 14;
|
|
}
|
|
|
|
/* журнал во весь экран: на низком окне списки и лог не помещаются вместе */
|
|
.zoom-log #board {
|
|
display: none;
|
|
}
|
|
|
|
.zoom-log #bottom {
|
|
height: 1fr !important;
|
|
max-height: 100% !important;
|
|
}
|
|
|
|
/* низкое окно: фиксированные 20 строк снизу не оставляют места спискам */
|
|
.short #bottom {
|
|
height: 35%;
|
|
min-height: 5;
|
|
}
|
|
|
|
.short #plan {
|
|
width: 28;
|
|
}
|
|
|
|
/* низкое окно: карточка приёмника — справочная, место нужнее спискам */
|
|
.short #target-card {
|
|
display: none;
|
|
}
|
|
|
|
.zoom-log.narrow #plan {
|
|
height: 25%;
|
|
min-height: 3;
|
|
}
|