diff --git a/widgets/multiagentselector.py b/widgets/multiagentselector.py index b6521d1..a06742d 100644 --- a/widgets/multiagentselector.py +++ b/widgets/multiagentselector.py @@ -1,6 +1,6 @@ import difflib -from textual.containers import Horizontal +from textual.containers import Horizontal, Vertical from textual.css.query import NoMatches from textual.message import Message from textual.widget import Widget @@ -16,7 +16,7 @@ class MultiAgentSelector(Widget): def __init__(self, all_agents: list[dict]): super().__init__() self.all_agents = all_agents - self._match_type = "fuzzy" + self._match_type = "exact" @property def match_type(self): @@ -27,43 +27,73 @@ class MultiAgentSelector(Widget): 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 + title_text = Static("🖧 Multi-Agent Selector", id="selector_title") + title_text.styles.margin = (0, 0, 0, 1) + yield title_text - 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 + with Horizontal() as main_layout: + main_layout.styles.height = "auto" - switch = Switch(value=False, id="match_switch") - switch.styles.width = "auto" - yield switch + # Left side - Input and controls + with Vertical() as left_pane: + left_pane.styles.width = "1fr" + left_pane.styles.height = "auto" - yield Button("Search", id="search_button") + text_area = TextArea( + id="device_input", + placeholder="Paste device names here (one per line). Supports wildcards: * and ?", + ) + text_area.styles.height = 10 + text_area.styles.overflow_y = "auto" + yield text_area - yield SelectionList(id="match_results") - yield Static(id="unmatched_label") + with Horizontal(id="switch_search_container") as switch_search: - yield Horizontal( - Button("Select All", id="select_all"), - Button("Select None", id="select_none"), - ) + switch = Switch(value=False, id="match_switch") + switch.styles.width = "auto" + switch.styles.margin = (1, 0, 0, 0) + switch.styles.padding = (0, 0, 0, 0) + yield switch - yield Button("Continue with Selected", id="submit_selection", variant="primary") + switch_label = Static("Match: Exact", id="match_switch_label") + switch_label.styles.width = "auto" + switch_label.styles.margin = (2, 1, 0, 0) + yield switch_label + + search = Button("🔍 Search", id="search_button") + search.styles.margin = (1, 0, 0, 0) + + yield search + + with Horizontal() as select_buttons: + select_buttons.styles.margin = (0, 0, 0, 0) + + select_all_button = Button("✅ Select All", id="select_all") + select_all_button.styles.margin = (1, 1, 0, 1) + yield select_all_button + + select_none_button = Button("🚫 Select None", id="select_none") + select_none_button.styles.margin = (1, 0, 0, 1) + yield select_none_button + + submit_button = Button( + "► Continue with Selected", id="submit_selection", variant="primary" + ) + submit_button.styles.margin = (0, 5, 2, 1) + submit_button.styles.padding = (0, 6, 0, 0) + yield submit_button + + # Right side - Results + with Vertical() as right_pane: + right_pane.styles.width = "2fr" + + yield SelectionList(id="match_results") + yield Static(id="unmatched_label") def on_switch_changed(self, event: Switch.Changed): - self.match_type = "exact" if event.value else "fuzzy" + self.match_type = "fuzzy" if event.value else "exact" self.query_one("#match_switch_label", Static).update( - f"Match Type: {self.match_type.capitalize()}" + f"Match: {self.match_type.capitalize()}" ) def on_button_pressed(self, event: Button.Pressed): @@ -111,7 +141,28 @@ class MultiAgentSelector(Widget): matched = set() unmatched = [] for name in device_names: - if self.match_type == "exact": + # Check if the name contains wildcards + has_wildcards = "*" in name or "?" in name + + if has_wildcards: + # Use regex for wildcard matching + import re + + # Escape special regex characters except * and ? + pattern = re.escape(name) + # Convert wildcards to regex + pattern = pattern.replace(r"\*", ".*").replace(r"\?", ".") + # Make it case-insensitive and match full string + regex = re.compile(f"^{pattern}$", re.IGNORECASE) + + wildcard_matches = [ + agent_name for agent_name in agent_names if regex.match(agent_name) + ] + if wildcard_matches: + matched.update(wildcard_matches) + else: + unmatched.append(name) + elif self.match_type == "exact": # Case-insensitive exact match name_lower = name.lower() exact_match = None