Add search and filter to the guest table
CI / pytest and ruff (pull_request) Successful in 4m48s
CI / pytest and ruff (push) Has been cancelled

/ opens a filter box under the guest table, and the table filters as
you type. Terms are separated by spaces, all must match, and case is
ignored:

- a plain term matches part of the name, the VMID or a tag
- is:running and is:stopped match the status
- type:lxc/ct and type:vm/qemu match the guest type

Enter keeps the filter and goes back to the table. Escape clears it,
from the box or from the table. The frame shows
"· filter: <text> · N of M". Actions only ever see the visible rows,
and the filter survives reloads and context switches.

Review fixes:
- Typing "[/b]" or "[/]" crashed the app, because the filter text went
  into the border subtitle, which Textual parses as markup. The
  subtitle is now plain text, so "[red]" is also shown literally.
- Escape on the table now clears a filter that was applied with Enter.
- guest_matches tolerates a None status or type.

Read-only live check on mainframe: "debian" gives 104, "type:vm" gives
108, "community-script is:running" gives 14 of 19, and "is:stopped"
gives 0 of 19.

Closes #16

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit was merged in pull request #29.
This commit is contained in:
DuckbootandClaude Opus 5.5 committed 2026-11-03 00:42:13 +01:00
1 parent ff6345c80f
commit d97ad22c78
4 files changed
+481 -7

No files matched your search

+28
View File
@@ -629,3 +629,31 @@ CLI facts (read-only probes):
is dropped after a switch) and is cleared when a switch starts. Failures leave the
count unknown (nothing shown), no error notify on mount.
- Main footer shows `U Updates`.
---
# Search and filter in the guest table (issue #16)
## Decision
- `/` on the main screen shows `Input#filter` (hidden until then) below the guest table
and focuses it. The table filters as you type.
- Filter text = space-separated terms, all must match (AND), case-insensitive:
- a plain term matches a substring of the name, the VMID or one of the tags
(`tags` from `get guests`, `;`-separated);
- `is:running` / `is:stopped` match the status;
- `type:lxc` / `type:ct` and `type:vm` / `type:qemu` match the guest type.
- Enter in the filter keeps the filter and moves focus to the table. Escape in the
filter clears it, hides the input and moves focus to the table.
- The frame's bottom border gets "· filter: <text> · N of M" while a filter is active,
after the context name and before the backup/package counts.
- Actions (s/S/x/r/t/Enter) act on the selected *visible* row only; the table only ever
contains the matching guests. A reload (g, after actions, context switch) keeps the
filter applied. A context switch keeps the filter text.
- Placeholder of the input shows the syntax: `name, vmid, tag · is:running · type:lxc`.
- No match: empty table, and the frame says "0 of M"; action keys do nothing.
### Code
- Pure function `guest_matches(guest: dict, query: str) -> bool` in `app.py` (or
`cli.py`-free module-level helper) so it can be tested without the UI.