Replace schedule source/destination lists with toggle switches, add DOCUMENTATION.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1066
DOCUMENTATION.md
Normal file
1066
DOCUMENTATION.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -395,6 +395,18 @@ Switch {
|
|||||||
margin: 0 1;
|
margin: 0 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sched-switch-row {
|
||||||
|
height: 3;
|
||||||
|
align: left middle;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sched-switch-label {
|
||||||
|
width: 1fr;
|
||||||
|
height: 3;
|
||||||
|
content-align: left middle;
|
||||||
|
}
|
||||||
|
|
||||||
.section-label {
|
.section-label {
|
||||||
text-style: bold;
|
text-style: bold;
|
||||||
color: #00cc00;
|
color: #00cc00;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import re
|
import re
|
||||||
from textual.app import ComposeResult
|
from textual.app import ComposeResult
|
||||||
from textual.screen import Screen
|
from textual.screen import Screen
|
||||||
from textual.widgets import Header, Footer, Static, Button, Input, Select, SelectionList
|
from textual.widgets import Header, Footer, Static, Button, Input, Select, SelectionList, Switch
|
||||||
from tui.widgets.header import GnizaHeader as Header # noqa: F811
|
from tui.widgets.header import GnizaHeader as Header # noqa: F811
|
||||||
from textual.containers import Vertical, Horizontal
|
from textual.containers import Vertical, Horizontal
|
||||||
|
|
||||||
@@ -105,27 +105,29 @@ class ScheduleEditScreen(Screen):
|
|||||||
placeholder="0 2 * * *",
|
placeholder="0 2 * * *",
|
||||||
classes="sched-cron-field",
|
classes="sched-cron-field",
|
||||||
)
|
)
|
||||||
yield Static("Sources (empty=all):")
|
yield Static("Sources (off=all):")
|
||||||
yield SelectionList[str](
|
for tname in list_conf_dir("targets.d"):
|
||||||
*self._build_target_choices(),
|
with Horizontal(classes="sched-switch-row"):
|
||||||
id="sched-targets",
|
yield Static(tname, classes="sched-switch-label")
|
||||||
)
|
yield Switch(value=False, id=f"sched-src-{tname}")
|
||||||
yield Static("Destinations (empty=all):")
|
yield Static("Destinations (off=all):")
|
||||||
yield SelectionList[str](
|
for rname in list_conf_dir("remotes.d"):
|
||||||
*self._build_remote_choices(),
|
with Horizontal(classes="sched-switch-row"):
|
||||||
id="sched-remotes",
|
yield Static(rname, classes="sched-switch-label")
|
||||||
)
|
yield Switch(value=False, id=f"sched-dst-{rname}")
|
||||||
with Horizontal(id="sched-edit-buttons"):
|
with Horizontal(id="sched-edit-buttons"):
|
||||||
yield Button("Save", variant="primary", id="btn-save")
|
yield Button("Save", variant="primary", id="btn-save")
|
||||||
yield Button("Cancel", id="btn-cancel")
|
yield Button("Cancel", id="btn-cancel")
|
||||||
yield DocsPanel.for_screen("schedule-edit")
|
yield DocsPanel.for_screen("schedule-edit")
|
||||||
yield Footer()
|
yield Footer()
|
||||||
|
|
||||||
def _build_target_choices(self) -> list[tuple[str, str]]:
|
def _get_selected_switches(self, prefix: str) -> list[str]:
|
||||||
return [(name, name) for name in list_conf_dir("targets.d")]
|
"""Return names of enabled switches matching the ID prefix."""
|
||||||
|
selected = []
|
||||||
def _build_remote_choices(self) -> list[tuple[str, str]]:
|
for sw in self.query(Switch):
|
||||||
return [(name, name) for name in list_conf_dir("remotes.d")]
|
if sw.id and sw.id.startswith(prefix) and sw.value:
|
||||||
|
selected.append(sw.id[len(prefix):])
|
||||||
|
return selected
|
||||||
|
|
||||||
def on_mount(self) -> None:
|
def on_mount(self) -> None:
|
||||||
self._update_type_visibility()
|
self._update_type_visibility()
|
||||||
@@ -144,22 +146,22 @@ class ScheduleEditScreen(Screen):
|
|||||||
opt = days_list.get_option_at_index(idx)
|
opt = days_list.get_option_at_index(idx)
|
||||||
if opt.value in day_vals:
|
if opt.value in day_vals:
|
||||||
days_list.select(opt.value)
|
days_list.select(opt.value)
|
||||||
# Targets
|
# Sources
|
||||||
if sched.targets:
|
if sched.targets:
|
||||||
target_vals = set(sched.targets.split(","))
|
for tname in sched.targets.split(","):
|
||||||
tlist = self.query_one("#sched-targets", SelectionList)
|
tname = tname.strip()
|
||||||
for idx in range(tlist.option_count):
|
try:
|
||||||
opt = tlist.get_option_at_index(idx)
|
self.query_one(f"#sched-src-{tname}", Switch).value = True
|
||||||
if opt.value in target_vals:
|
except Exception:
|
||||||
tlist.select(opt.value)
|
pass
|
||||||
# Remotes
|
# Destinations
|
||||||
if sched.remotes:
|
if sched.remotes:
|
||||||
remote_vals = set(sched.remotes.split(","))
|
for rname in sched.remotes.split(","):
|
||||||
rlist = self.query_one("#sched-remotes", SelectionList)
|
rname = rname.strip()
|
||||||
for idx in range(rlist.option_count):
|
try:
|
||||||
opt = rlist.get_option_at_index(idx)
|
self.query_one(f"#sched-dst-{rname}", Switch).value = True
|
||||||
if opt.value in remote_vals:
|
except Exception:
|
||||||
rlist.select(opt.value)
|
pass
|
||||||
|
|
||||||
def on_select_changed(self, event: Select.Changed) -> None:
|
def on_select_changed(self, event: Select.Changed) -> None:
|
||||||
if event.select.id == "sched-type":
|
if event.select.id == "sched-type":
|
||||||
@@ -226,8 +228,8 @@ class ScheduleEditScreen(Screen):
|
|||||||
time=self.query_one("#sched-time", Input).value.strip() or "02:00",
|
time=self.query_one("#sched-time", Input).value.strip() or "02:00",
|
||||||
day=day_val,
|
day=day_val,
|
||||||
cron=self.query_one("#sched-cron", Input).value.strip(),
|
cron=self.query_one("#sched-cron", Input).value.strip(),
|
||||||
targets=",".join(self.query_one("#sched-targets", SelectionList).selected),
|
targets=",".join(self._get_selected_switches("sched-src-")),
|
||||||
remotes=",".join(self.query_one("#sched-remotes", SelectionList).selected),
|
remotes=",".join(self._get_selected_switches("sched-dst-")),
|
||||||
)
|
)
|
||||||
conf = CONFIG_DIR / "schedules.d" / f"{name}.conf"
|
conf = CONFIG_DIR / "schedules.d" / f"{name}.conf"
|
||||||
write_conf(conf, sched.to_conf())
|
write_conf(conf, sched.to_conf())
|
||||||
|
|||||||
Reference in New Issue
Block a user