Multiselect appears to be working mostly correctly and is styled. It is only temp a tab, as this is going to be an intermediate/ephem widget

This commit is contained in:
2025-11-06 20:26:06 -05:00
parent 8c44de204d
commit 9dfdf8b9ee
+82 -31
View File
@@ -1,6 +1,6 @@
import difflib import difflib
from textual.containers import Horizontal from textual.containers import Horizontal, Vertical
from textual.css.query import NoMatches from textual.css.query import NoMatches
from textual.message import Message from textual.message import Message
from textual.widget import Widget from textual.widget import Widget
@@ -16,7 +16,7 @@ class MultiAgentSelector(Widget):
def __init__(self, all_agents: list[dict]): def __init__(self, all_agents: list[dict]):
super().__init__() super().__init__()
self.all_agents = all_agents self.all_agents = all_agents
self._match_type = "fuzzy" self._match_type = "exact"
@property @property
def match_type(self): def match_type(self):
@@ -27,43 +27,73 @@ class MultiAgentSelector(Widget):
self._match_type = value self._match_type = value
def compose(self): def compose(self):
yield Static("🔍 Multi-Agent Selector", id="selector_title") title_text = Static("🖧 Multi-Agent Selector", id="selector_title")
text_area = TextArea( title_text.styles.margin = (0, 0, 0, 1)
id="device_input", yield title_text
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: with Horizontal() as main_layout:
switch_container.styles.height = "auto" main_layout.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") # Left side - Input and controls
switch.styles.width = "auto" with Vertical() as left_pane:
yield switch 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") with Horizontal(id="switch_search_container") as switch_search:
yield Static(id="unmatched_label")
yield Horizontal( switch = Switch(value=False, id="match_switch")
Button("Select All", id="select_all"), switch.styles.width = "auto"
Button("Select None", id="select_none"), 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): 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( 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): def on_button_pressed(self, event: Button.Pressed):
@@ -111,7 +141,28 @@ class MultiAgentSelector(Widget):
matched = set() matched = set()
unmatched = [] unmatched = []
for name in device_names: 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 # Case-insensitive exact match
name_lower = name.lower() name_lower = name.lower()
exact_match = None exact_match = None