Device search is working. Working on The multidevice seach, WIP

This commit is contained in:
2025-11-06 17:36:45 -05:00
parent c062532dd6
commit 26199c72cb
5 changed files with 423 additions and 105 deletions
+133
View File
@@ -0,0 +1,133 @@
import difflib
from textual.containers import Horizontal
from textual.css.query import NoMatches
from textual.message import Message
from textual.widget import Widget
from textual.widgets import Button, SelectionList, Static, Switch, TextArea
class MultiAgentSelector(Widget):
class AgentsSelected(Message):
def __init__(self, selected_agents):
super().__init__()
self.selected_agents = selected_agents
def __init__(self, all_agents: list[dict]):
super().__init__()
self.all_agents = all_agents
self._match_type = "fuzzy"
@property
def match_type(self):
return self._match_type
@match_type.setter
def match_type(self, value):
self._match_type = value
def compose(self):
yield Static("🔍 Multi-Agent Selector", id="selector_title")
text_area = TextArea(
id="device_input",
placeholder="Paste device names here (one per line)",
)
text_area.styles.height = 10
text_area.styles.overflow_y = "auto"
yield text_area
with Horizontal() as switch_container:
switch_container.styles.height = "auto"
switch_container.styles.align = ("left", "middle")
switch_label = Static("Match Type: Fuzzy", id="match_switch_label")
switch_label.styles.width = "auto"
switch_label.styles.padding = (0, 1)
yield switch_label
switch = Switch(value=False, id="match_switch")
switch.styles.width = "auto"
yield switch
yield Button("Search", id="search_button")
yield SelectionList(id="match_results")
yield Static(id="unmatched_label")
yield Horizontal(
Button("Select All", id="select_all"),
Button("Select None", id="select_none"),
)
yield Button("Continue with Selected", id="submit_selection", variant="primary")
def on_switch_changed(self, event: Switch.Changed):
self.match_type = "exact" if event.value else "fuzzy"
self.query_one("#match_switch_label", Static).update(
f"Match Type: {self.match_type.capitalize()}"
)
def on_button_pressed(self, event: Button.Pressed):
btn_id = event.button.id # can be None for internal buttons
# Only query when needed
try:
match_list = self.query_one("#match_results", SelectionList)
except NoMatches:
# UI not mounted yet or id changed—just ignore gracefully
return
if btn_id == "select_all":
match_list.select_all()
event.stop()
elif btn_id == "select_none":
match_list.deselect_all()
event.stop()
elif btn_id == "submit_selection":
selected = list(match_list.selected)
self.post_message(self.AgentsSelected(selected))
event.stop()
elif btn_id == "search_button":
self.update_matches()
event.stop()
def update_matches(self):
raw_input = self.query_one("#device_input", TextArea).text.strip()
device_names = [line.strip() for line in raw_input.split("\n") if line.strip()]
matched, unmatched = self.match_devices(device_names)
match_list = self.query_one("#match_results", SelectionList)
match_list.clear_options()
for name in matched:
match_list.add_option((name, name))
unmatched_label = self.query_one("#unmatched_label", Static)
if unmatched:
unmatched_label.update(f"⚠️ No matches for: {', '.join(unmatched)}")
else:
unmatched_label.update("")
def match_devices(self, device_names: list[str]) -> tuple[list[str], list[str]]:
if not self.all_agents or not device_names:
return [], device_names
agent_names = [agent["hostname"] for agent in self.all_agents]
matched = set()
unmatched = []
for name in device_names:
if self.match_type == "exact":
# Case-insensitive exact match
name_lower = name.lower()
exact_match = None
for agent_name in agent_names:
if agent_name.lower() == name_lower:
exact_match = agent_name
break
if exact_match:
matched.add(exact_match)
else:
unmatched.append(name)
else:
# Fuzzy match
matches = difflib.get_close_matches(name, agent_names, n=5, cutoff=0.5)
if matches:
matched.update(matches)
else:
unmatched.append(name)
return sorted(matched), unmatched