dlab-cli 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,170 @@
1
+ """
2
+ Agent selector sidebar widget.
3
+
4
+ Displays list of agents/sources with status indicators.
5
+ """
6
+
7
+ import re
8
+
9
+ from textual.widgets import ListView, ListItem, Static
10
+ from textual.reactive import reactive
11
+ from textual.message import Message
12
+ from rich.text import Text
13
+
14
+
15
+ def shorten_agent_name(name: str) -> str:
16
+ """
17
+ Shorten agent names for display.
18
+
19
+ Converts "poet-parallel-run-1234567890/instance-1" to "⟝ poet (…90) / inst-1".
20
+ Converts "poet-parallel-run-1234567890/consolidator" to "⟝ poet (…90) / cnsldtr".
21
+
22
+ Parameters
23
+ ----------
24
+ name : str
25
+ Full agent name.
26
+
27
+ Returns
28
+ -------
29
+ str
30
+ Shortened display name.
31
+ """
32
+ # Match pattern: agent-parallel-run-<number>/suffix
33
+ match = re.match(r"^(.+)-parallel-run-(\d+)/(.+)$", name)
34
+ if match:
35
+ agent = match.group(1)
36
+ number = match.group(2)
37
+ suffix = match.group(3)
38
+
39
+ # Shorten suffix
40
+ if suffix.startswith("instance-"):
41
+ suffix = "inst-" + suffix[9:]
42
+ elif suffix == "consolidator":
43
+ suffix = "cnsldtr"
44
+ elif suffix.startswith("consolidator-"):
45
+ suffix = "cnsldtr-" + suffix[13:]
46
+
47
+ return f"⟝ {agent} …{number[-2:]}/ {suffix}"
48
+ return name
49
+
50
+
51
+ class AgentListItem(ListItem):
52
+ """
53
+ List item for a single agent.
54
+
55
+ Displays agent name with status indicator.
56
+ """
57
+
58
+ def __init__(self, name: str, agent_running: bool = True) -> None:
59
+ """
60
+ Initialize agent list item.
61
+
62
+ Parameters
63
+ ----------
64
+ name : str
65
+ Agent name.
66
+ agent_running : bool
67
+ Whether agent is still running.
68
+ """
69
+ super().__init__()
70
+ self.agent_name = name
71
+ self.agent_running = agent_running
72
+
73
+ def compose(self):
74
+ """Compose the widget."""
75
+ if self.agent_running:
76
+ indicator = Text("● ", style="bold #A6E22E")
77
+ else:
78
+ indicator = Text("○ ", style="#75715E")
79
+
80
+ name_style = "" if self.agent_running else "#75715E"
81
+ display_name = shorten_agent_name(self.agent_name)
82
+ name_text = Text(display_name, style=name_style)
83
+
84
+ yield Static(indicator + name_text)
85
+
86
+
87
+ class AgentSelector(ListView):
88
+ """
89
+ Sidebar list widget for selecting log sources.
90
+
91
+ Features:
92
+ - Show running agents with green indicator
93
+ - Grey out completed agents
94
+ - Natural sort order (main first, instances, consolidator last)
95
+ - Click/keyboard to select agent
96
+ """
97
+
98
+ class AgentSelected(Message):
99
+ """Message sent when an agent is selected."""
100
+
101
+ def __init__(self, agent_name: str) -> None:
102
+ self.agent_name = agent_name
103
+ super().__init__()
104
+
105
+ selected_agent: reactive[str | None] = reactive(None)
106
+
107
+ def __init__(self, *args, **kwargs) -> None:
108
+ super().__init__(*args, **kwargs)
109
+ self._agents: dict[str, bool] = {} # name -> is_running
110
+
111
+ def update_agents(
112
+ self,
113
+ agents: list[str],
114
+ running: set[str],
115
+ ) -> None:
116
+ """
117
+ Update the agent list with current states.
118
+
119
+ Parameters
120
+ ----------
121
+ agents : list[str]
122
+ All agent names (should be pre-sorted).
123
+ running : set[str]
124
+ Names of currently running agents.
125
+ """
126
+ # Track what changed
127
+ new_agents = {name: name in running for name in agents}
128
+
129
+ if new_agents == self._agents:
130
+ return
131
+
132
+ self._agents = new_agents
133
+
134
+ # Rebuild list
135
+ self.clear()
136
+ for name in agents:
137
+ running_status = name in running
138
+ item = AgentListItem(name, agent_running=running_status)
139
+ self.append(item)
140
+
141
+ # Re-select if needed
142
+ if self.selected_agent and self.selected_agent in self._agents:
143
+ self._select_by_name(self.selected_agent)
144
+
145
+ def _select_by_name(self, name: str) -> None:
146
+ """Select agent by name."""
147
+ for i, item in enumerate(self.children):
148
+ if isinstance(item, AgentListItem) and item.agent_name == name:
149
+ self.index = i
150
+ break
151
+
152
+ def on_list_view_selected(self, event: ListView.Selected) -> None:
153
+ """Handle selection (Enter key or click)."""
154
+ if isinstance(event.item, AgentListItem):
155
+ self.selected_agent = event.item.agent_name
156
+ self.post_message(self.AgentSelected(event.item.agent_name))
157
+
158
+ def on_list_view_highlighted(self, event: ListView.Highlighted) -> None:
159
+ """Handle highlight change (arrow key navigation)."""
160
+ if isinstance(event.item, AgentListItem):
161
+ self.selected_agent = event.item.agent_name
162
+ self.post_message(self.AgentSelected(event.item.agent_name))
163
+
164
+ def select_first(self) -> None:
165
+ """Select the first agent."""
166
+ if self._agents:
167
+ first_name = next(iter(self._agents.keys()))
168
+ self.selected_agent = first_name
169
+ self._select_by_name(first_name)
170
+ self.post_message(self.AgentSelected(first_name))