"""Commands page — read-only browser of `commands.yaml` + plugin packs.""" import os import flet as ft import yaml _HERE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) def _load_commands() -> dict[str, dict]: try: with open(os.path.join(_HERE, "commands.yaml"), "rt", encoding="utf8") as f: return yaml.safe_load(f) or {} except (OSError, yaml.YAMLError): return {} def build(page: ft.Page) -> ft.Control: # pylint: disable=unused-argument cmds = _load_commands() search = ft.TextField(label="Фильтр", value="", autofocus=False, expand=True) list_view = ft.ListView(expand=True, spacing=6, padding=0) def render(query: str = ""): q = (query or "").strip().lower() list_view.controls.clear() shown = 0 for cmd_id, entry in sorted(cmds.items()): phrases = entry.get("phrases") or [] action = entry.get("action") or {} joined = " ".join(phrases).lower() if q and q not in cmd_id.lower() and q not in joined: continue shown += 1 list_view.controls.append( ft.Container( content=ft.Column([ ft.Row([ ft.Text(cmd_id, weight=ft.FontWeight.W_600, size=13), ft.Container( content=ft.Text(action.get("type", "?"), size=10), bgcolor="#1e3a44", padding=ft.padding.symmetric(horizontal=8, vertical=2), border_radius=10, ), ], alignment=ft.MainAxisAlignment.SPACE_BETWEEN), ft.Text( " · ".join(phrases[:6]) + ("…" if len(phrases) > 6 else ""), size=11, color="#8aa0a8", ), ], spacing=4), bgcolor="#15252b", border_radius=8, padding=10, ) ) # The header refers to the closure-captured `header` Text via the # outer-scope binding declared after this function. header.value = f"Команд показано: {shown} из {len(cmds)}" page.update() search.on_change = lambda e: render(e.control.value) header = ft.Text(f"Всего команд: {len(cmds)}", size=12, color="#8aa0a8") render() return ft.Column( controls=[ ft.Row([ ft.Text("Команды", size=24, weight=ft.FontWeight.W_700), header, ], alignment=ft.MainAxisAlignment.SPACE_BETWEEN), search, ft.Container(content=list_view, expand=True), ], spacing=10, expand=True, )