uipath-dev 0.0.1__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.
- uipath/dev/__init__.py +329 -0
- uipath/dev/_demo/__init__.py +0 -0
- uipath/dev/_demo/mock_runtime.py +64 -0
- uipath/dev/_demo/run_dev_console.py +15 -0
- uipath/dev/_styles/terminal.tcss +261 -0
- uipath/dev/_utils/_exporter.py +119 -0
- uipath/dev/_utils/_logger.py +98 -0
- uipath/dev/components/details.py +453 -0
- uipath/dev/components/history.py +110 -0
- uipath/dev/components/json_input.py +27 -0
- uipath/dev/components/new.py +142 -0
- uipath/dev/models/execution.py +80 -0
- uipath/dev/models/messages.py +53 -0
- uipath/dev/py.typed +0 -0
- uipath_dev-0.0.1.dist-info/METADATA +59 -0
- uipath_dev-0.0.1.dist-info/RECORD +19 -0
- uipath_dev-0.0.1.dist-info/WHEEL +4 -0
- uipath_dev-0.0.1.dist-info/entry_points.txt +2 -0
- uipath_dev-0.0.1.dist-info/licenses/LICENSE +9 -0
uipath/dev/__init__.py
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"""UiPath Dev Terminal Application."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
import traceback
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Optional
|
|
9
|
+
|
|
10
|
+
import pyperclip # type: ignore[import-untyped]
|
|
11
|
+
from pydantic import BaseModel
|
|
12
|
+
from rich.traceback import Traceback
|
|
13
|
+
from textual import on
|
|
14
|
+
from textual.app import App, ComposeResult
|
|
15
|
+
from textual.binding import Binding
|
|
16
|
+
from textual.containers import Container, Horizontal
|
|
17
|
+
from textual.widgets import Button, Footer, Input, ListView, RichLog
|
|
18
|
+
from uipath.core.tracing import UiPathTraceManager
|
|
19
|
+
from uipath.runtime import (
|
|
20
|
+
UiPathExecuteOptions,
|
|
21
|
+
UiPathExecutionRuntime,
|
|
22
|
+
UiPathRuntimeFactory,
|
|
23
|
+
UiPathRuntimeStatus,
|
|
24
|
+
)
|
|
25
|
+
from uipath.runtime.errors import UiPathErrorContract, UiPathRuntimeError
|
|
26
|
+
|
|
27
|
+
from uipath.dev.components.details import RunDetailsPanel
|
|
28
|
+
from uipath.dev.components.history import RunHistoryPanel
|
|
29
|
+
from uipath.dev.components.new import NewRunPanel
|
|
30
|
+
from uipath.dev.models.execution import ExecutionRun
|
|
31
|
+
from uipath.dev.models.messages import LogMessage, TraceMessage
|
|
32
|
+
|
|
33
|
+
from ._utils._exporter import RunContextExporter
|
|
34
|
+
from ._utils._logger import patch_textual_stderr
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class UiPathDeveloperConsole(App[Any]):
|
|
38
|
+
"""UiPath debugging terminal interface."""
|
|
39
|
+
|
|
40
|
+
TITLE = "UiPath Debugging Terminal"
|
|
41
|
+
SUB_TITLE = "Interactive debugging interface for UiPath Python projects"
|
|
42
|
+
CSS_PATH = Path(__file__).parent / "_styles" / "terminal.tcss"
|
|
43
|
+
|
|
44
|
+
BINDINGS = [
|
|
45
|
+
Binding("q", "quit", "Quit"),
|
|
46
|
+
Binding("n", "new_run", "New"),
|
|
47
|
+
Binding("r", "execute_run", "Run"),
|
|
48
|
+
Binding("c", "copy", "Copy"),
|
|
49
|
+
Binding("h", "clear_history", "Clear History"),
|
|
50
|
+
Binding("escape", "cancel", "Cancel"),
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
def __init__(
|
|
54
|
+
self,
|
|
55
|
+
runtime_factory: UiPathRuntimeFactory[Any],
|
|
56
|
+
trace_manager: UiPathTraceManager,
|
|
57
|
+
**kwargs,
|
|
58
|
+
):
|
|
59
|
+
"""Initialize the UiPath Dev Terminal App."""
|
|
60
|
+
self._stderr_write_fd: int = patch_textual_stderr(self._add_subprocess_log)
|
|
61
|
+
|
|
62
|
+
super().__init__(**kwargs)
|
|
63
|
+
|
|
64
|
+
self.initial_entrypoint: str = "main.py"
|
|
65
|
+
self.initial_input: str = '{\n "message": "Hello World"\n}'
|
|
66
|
+
self.runs: dict[str, ExecutionRun] = {}
|
|
67
|
+
self.runtime_factory = runtime_factory
|
|
68
|
+
self.trace_manager = trace_manager
|
|
69
|
+
self.trace_manager.add_span_exporter(
|
|
70
|
+
RunContextExporter(
|
|
71
|
+
on_trace=self._handle_trace_message,
|
|
72
|
+
on_log=self._handle_log_message,
|
|
73
|
+
),
|
|
74
|
+
batch=False,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def compose(self) -> ComposeResult:
|
|
78
|
+
"""Compose the UI layout."""
|
|
79
|
+
with Horizontal():
|
|
80
|
+
# Left sidebar - run history
|
|
81
|
+
with Container(classes="run-history"):
|
|
82
|
+
yield RunHistoryPanel(id="history-panel")
|
|
83
|
+
|
|
84
|
+
# Main content area
|
|
85
|
+
with Container(classes="main-content"):
|
|
86
|
+
# New run panel (initially visible)
|
|
87
|
+
yield NewRunPanel(
|
|
88
|
+
id="new-run-panel",
|
|
89
|
+
classes="new-run-panel",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Run details panel (initially hidden)
|
|
93
|
+
yield RunDetailsPanel(id="details-panel", classes="hidden")
|
|
94
|
+
|
|
95
|
+
yield Footer()
|
|
96
|
+
|
|
97
|
+
async def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
98
|
+
"""Handle button press events."""
|
|
99
|
+
if event.button.id == "new-run-btn":
|
|
100
|
+
await self.action_new_run()
|
|
101
|
+
elif event.button.id == "execute-btn":
|
|
102
|
+
await self.action_execute_run()
|
|
103
|
+
elif event.button.id == "cancel-btn":
|
|
104
|
+
await self.action_cancel()
|
|
105
|
+
|
|
106
|
+
async def on_list_view_selected(self, event: ListView.Selected) -> None:
|
|
107
|
+
"""Handle run selection from history."""
|
|
108
|
+
if event.list_view.id == "run-list" and event.item:
|
|
109
|
+
run_id = getattr(event.item, "run_id", None)
|
|
110
|
+
if run_id:
|
|
111
|
+
history_panel = self.query_one("#history-panel", RunHistoryPanel)
|
|
112
|
+
run = history_panel.get_run_by_id(run_id)
|
|
113
|
+
if run:
|
|
114
|
+
self._show_run_details(run)
|
|
115
|
+
|
|
116
|
+
@on(Input.Submitted, "#chat-input")
|
|
117
|
+
async def handle_chat_input(self, event: Input.Submitted) -> None:
|
|
118
|
+
"""Handle user submitting text into the chat."""
|
|
119
|
+
user_text = event.value.strip()
|
|
120
|
+
if not user_text:
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
details_panel = self.query_one("#details-panel", RunDetailsPanel)
|
|
124
|
+
if details_panel and details_panel.current_run:
|
|
125
|
+
status = details_panel.current_run.status
|
|
126
|
+
if status == "running":
|
|
127
|
+
self.app.notify(
|
|
128
|
+
"Wait for agent response...", timeout=1.5, severity="warning"
|
|
129
|
+
)
|
|
130
|
+
return
|
|
131
|
+
if details_panel.current_run.status == "suspended":
|
|
132
|
+
details_panel.current_run.resume_data = {"message": user_text}
|
|
133
|
+
asyncio.create_task(self._execute_runtime(details_panel.current_run))
|
|
134
|
+
event.input.clear()
|
|
135
|
+
|
|
136
|
+
async def action_new_run(self) -> None:
|
|
137
|
+
"""Show new run panel."""
|
|
138
|
+
new_panel = self.query_one("#new-run-panel")
|
|
139
|
+
details_panel = self.query_one("#details-panel")
|
|
140
|
+
|
|
141
|
+
new_panel.remove_class("hidden")
|
|
142
|
+
details_panel.add_class("hidden")
|
|
143
|
+
|
|
144
|
+
async def action_cancel(self) -> None:
|
|
145
|
+
"""Cancel and return to new run view."""
|
|
146
|
+
await self.action_new_run()
|
|
147
|
+
|
|
148
|
+
async def action_execute_run(self) -> None:
|
|
149
|
+
"""Execute a new run with UiPath runtime."""
|
|
150
|
+
new_run_panel = self.query_one("#new-run-panel", NewRunPanel)
|
|
151
|
+
entrypoint, input_data, conversational = new_run_panel.get_input_values()
|
|
152
|
+
|
|
153
|
+
if not entrypoint:
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
input: dict[str, Any] = {}
|
|
157
|
+
try:
|
|
158
|
+
input = json.loads(input_data)
|
|
159
|
+
except json.JSONDecodeError:
|
|
160
|
+
return
|
|
161
|
+
|
|
162
|
+
run = ExecutionRun(entrypoint, input, conversational)
|
|
163
|
+
|
|
164
|
+
self.runs[run.id] = run
|
|
165
|
+
|
|
166
|
+
self._add_run_in_history(run)
|
|
167
|
+
|
|
168
|
+
self._show_run_details(run)
|
|
169
|
+
|
|
170
|
+
if not run.conversational:
|
|
171
|
+
asyncio.create_task(self._execute_runtime(run))
|
|
172
|
+
else:
|
|
173
|
+
self._focus_chat_input()
|
|
174
|
+
|
|
175
|
+
async def action_clear_history(self) -> None:
|
|
176
|
+
"""Clear run history."""
|
|
177
|
+
history_panel = self.query_one("#history-panel", RunHistoryPanel)
|
|
178
|
+
history_panel.clear_runs()
|
|
179
|
+
await self.action_new_run()
|
|
180
|
+
|
|
181
|
+
def action_copy(self) -> None:
|
|
182
|
+
"""Copy content of currently focused RichLog to clipboard and notify."""
|
|
183
|
+
focused = self.app.focused
|
|
184
|
+
if isinstance(focused, RichLog):
|
|
185
|
+
clipboard_text = "\n".join(line.text for line in focused.lines)
|
|
186
|
+
pyperclip.copy(clipboard_text)
|
|
187
|
+
self.app.notify("Copied to clipboard!", timeout=1.5)
|
|
188
|
+
else:
|
|
189
|
+
self.app.notify("Nothing to copy here.", timeout=1.5, severity="warning")
|
|
190
|
+
|
|
191
|
+
async def _execute_runtime(self, run: ExecutionRun):
|
|
192
|
+
"""Execute the script using UiPath runtime."""
|
|
193
|
+
try:
|
|
194
|
+
execution_input: Optional[dict[str, Any]] = {}
|
|
195
|
+
execution_options: UiPathExecuteOptions = UiPathExecuteOptions()
|
|
196
|
+
if run.status == "suspended":
|
|
197
|
+
execution_input = run.resume_data
|
|
198
|
+
execution_options.resume = True
|
|
199
|
+
self._add_info_log(run, f"Resuming execution: {run.entrypoint}")
|
|
200
|
+
else:
|
|
201
|
+
execution_input = run.input_data
|
|
202
|
+
self._add_info_log(run, f"Starting execution: {run.entrypoint}")
|
|
203
|
+
|
|
204
|
+
run.status = "running"
|
|
205
|
+
run.start_time = datetime.now()
|
|
206
|
+
|
|
207
|
+
runtime = self.runtime_factory.new_runtime(entrypoint=run.entrypoint)
|
|
208
|
+
execution_runtime = UiPathExecutionRuntime(
|
|
209
|
+
delegate=runtime, trace_manager=self.trace_manager, execution_id=run.id
|
|
210
|
+
)
|
|
211
|
+
result = await execution_runtime.execute(execution_input, execution_options)
|
|
212
|
+
|
|
213
|
+
if result is not None:
|
|
214
|
+
if (
|
|
215
|
+
result.status == UiPathRuntimeStatus.SUSPENDED.value
|
|
216
|
+
and result.resume
|
|
217
|
+
):
|
|
218
|
+
run.status = "suspended"
|
|
219
|
+
else:
|
|
220
|
+
if result.output is None:
|
|
221
|
+
run.output_data = {}
|
|
222
|
+
elif isinstance(result.output, BaseModel):
|
|
223
|
+
run.output_data = result.output.model_dump()
|
|
224
|
+
else:
|
|
225
|
+
run.output_data = result.output
|
|
226
|
+
run.status = "completed"
|
|
227
|
+
if run.output_data:
|
|
228
|
+
self._add_info_log(run, f"Execution result: {run.output_data}")
|
|
229
|
+
|
|
230
|
+
self._add_info_log(run, "✅ Execution completed successfully")
|
|
231
|
+
run.end_time = datetime.now()
|
|
232
|
+
|
|
233
|
+
except UiPathRuntimeError as e:
|
|
234
|
+
self._add_error_log(run)
|
|
235
|
+
run.status = "failed"
|
|
236
|
+
run.end_time = datetime.now()
|
|
237
|
+
run.error = e.error_info
|
|
238
|
+
|
|
239
|
+
except Exception as e:
|
|
240
|
+
self._add_error_log(run)
|
|
241
|
+
run.status = "failed"
|
|
242
|
+
run.end_time = datetime.now()
|
|
243
|
+
run.error = UiPathErrorContract(
|
|
244
|
+
code="Unknown", title=str(e), detail=traceback.format_exc()
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
self._update_run_in_history(run)
|
|
248
|
+
self._update_run_details(run)
|
|
249
|
+
|
|
250
|
+
def _show_run_details(self, run: ExecutionRun):
|
|
251
|
+
"""Show details panel for a specific run."""
|
|
252
|
+
# Hide new run panel, show details panel
|
|
253
|
+
new_panel = self.query_one("#new-run-panel")
|
|
254
|
+
details_panel = self.query_one("#details-panel", RunDetailsPanel)
|
|
255
|
+
|
|
256
|
+
new_panel.add_class("hidden")
|
|
257
|
+
details_panel.remove_class("hidden")
|
|
258
|
+
|
|
259
|
+
# Populate the details panel with run data
|
|
260
|
+
details_panel.update_run(run)
|
|
261
|
+
|
|
262
|
+
def _focus_chat_input(self):
|
|
263
|
+
"""Focus the chat input box."""
|
|
264
|
+
details_panel = self.query_one("#details-panel", RunDetailsPanel)
|
|
265
|
+
details_panel.switch_tab("chat-tab")
|
|
266
|
+
chat_input = details_panel.query_one("#chat-input", Input)
|
|
267
|
+
chat_input.focus()
|
|
268
|
+
|
|
269
|
+
def _add_run_in_history(self, run: ExecutionRun):
|
|
270
|
+
"""Add run to history panel."""
|
|
271
|
+
history_panel = self.query_one("#history-panel", RunHistoryPanel)
|
|
272
|
+
history_panel.add_run(run)
|
|
273
|
+
|
|
274
|
+
def _update_run_in_history(self, run: ExecutionRun):
|
|
275
|
+
"""Update run display in history panel."""
|
|
276
|
+
history_panel = self.query_one("#history-panel", RunHistoryPanel)
|
|
277
|
+
history_panel.update_run(run)
|
|
278
|
+
|
|
279
|
+
def _update_run_details(self, run: ExecutionRun):
|
|
280
|
+
"""Update the displayed run information."""
|
|
281
|
+
details_panel = self.query_one("#details-panel", RunDetailsPanel)
|
|
282
|
+
details_panel.update_run_details(run)
|
|
283
|
+
|
|
284
|
+
def _handle_trace_message(self, trace_msg: TraceMessage):
|
|
285
|
+
"""Handle trace message from exporter."""
|
|
286
|
+
run = self.runs[trace_msg.run_id]
|
|
287
|
+
for i, existing_trace in enumerate(run.traces):
|
|
288
|
+
if existing_trace.span_id == trace_msg.span_id:
|
|
289
|
+
run.traces[i] = trace_msg
|
|
290
|
+
break
|
|
291
|
+
else:
|
|
292
|
+
run.traces.append(trace_msg)
|
|
293
|
+
|
|
294
|
+
details_panel = self.query_one("#details-panel", RunDetailsPanel)
|
|
295
|
+
details_panel.add_trace(trace_msg)
|
|
296
|
+
|
|
297
|
+
def _handle_log_message(self, log_msg: LogMessage):
|
|
298
|
+
"""Handle log message from exporter."""
|
|
299
|
+
self.runs[log_msg.run_id].logs.append(log_msg)
|
|
300
|
+
details_panel = self.query_one("#details-panel", RunDetailsPanel)
|
|
301
|
+
details_panel.add_log(log_msg)
|
|
302
|
+
|
|
303
|
+
def _add_info_log(self, run: ExecutionRun, message: str):
|
|
304
|
+
"""Add info log to run."""
|
|
305
|
+
timestamp = datetime.now()
|
|
306
|
+
log_msg = LogMessage(run.id, "INFO", message, timestamp)
|
|
307
|
+
self._handle_log_message(log_msg)
|
|
308
|
+
|
|
309
|
+
def _add_error_log(self, run: ExecutionRun):
|
|
310
|
+
"""Add error log to run."""
|
|
311
|
+
timestamp = datetime.now()
|
|
312
|
+
tb = Traceback(
|
|
313
|
+
show_locals=False,
|
|
314
|
+
max_frames=4,
|
|
315
|
+
)
|
|
316
|
+
log_msg = LogMessage(run.id, "ERROR", tb, timestamp)
|
|
317
|
+
self._handle_log_message(log_msg)
|
|
318
|
+
|
|
319
|
+
def _add_subprocess_log(self, level: str, message: str) -> None:
|
|
320
|
+
"""Handle a stderr line coming from subprocesses."""
|
|
321
|
+
|
|
322
|
+
def add_log() -> None:
|
|
323
|
+
details_panel = self.query_one("#details-panel", RunDetailsPanel)
|
|
324
|
+
run = getattr(details_panel, "current_run", None)
|
|
325
|
+
if run:
|
|
326
|
+
log_msg = LogMessage(run.id, level, message, datetime.now())
|
|
327
|
+
self._handle_log_message(log_msg)
|
|
328
|
+
|
|
329
|
+
self.call_from_thread(add_log)
|
|
File without changes
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Minimal demo script to run UiPathDevTerminal with mock runtimes."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from typing import Any, Optional
|
|
5
|
+
|
|
6
|
+
from uipath.runtime import (
|
|
7
|
+
UiPathBaseRuntime,
|
|
8
|
+
UiPathExecuteOptions,
|
|
9
|
+
UiPathRuntimeFactory,
|
|
10
|
+
UiPathRuntimeResult,
|
|
11
|
+
UiPathRuntimeStatus,
|
|
12
|
+
)
|
|
13
|
+
from uipath.runtime.schema import UiPathRuntimeSchema
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MockRuntime(UiPathBaseRuntime):
|
|
17
|
+
"""A simple mock runtime that echoes its input."""
|
|
18
|
+
|
|
19
|
+
async def get_schema(self) -> UiPathRuntimeSchema:
|
|
20
|
+
return UiPathRuntimeSchema(
|
|
21
|
+
filePath="default",
|
|
22
|
+
uniqueId="mock-runtime",
|
|
23
|
+
type="agent",
|
|
24
|
+
input={
|
|
25
|
+
"type": "object",
|
|
26
|
+
"properties": {"message": {"type": "string"}},
|
|
27
|
+
"required": ["message"],
|
|
28
|
+
},
|
|
29
|
+
output={
|
|
30
|
+
"type": "object",
|
|
31
|
+
"properties": {"result": {"type": "string"}},
|
|
32
|
+
"required": ["result"],
|
|
33
|
+
},
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
async def execute(
|
|
37
|
+
self,
|
|
38
|
+
input: Optional[dict[str, Any]] = None,
|
|
39
|
+
options: Optional[UiPathExecuteOptions] = None,
|
|
40
|
+
) -> UiPathRuntimeResult:
|
|
41
|
+
payload = input or {}
|
|
42
|
+
# Simulate some async work
|
|
43
|
+
await asyncio.sleep(0.2)
|
|
44
|
+
return UiPathRuntimeResult(
|
|
45
|
+
output={"result": f"Mock runtime got: {payload!r}"},
|
|
46
|
+
status=UiPathRuntimeStatus.SUCCESSFUL,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
async def cleanup(self) -> None:
|
|
50
|
+
# Nothing to clean up in this mock
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# 2) Mock runtime factory
|
|
55
|
+
class MockRuntimeFactory(UiPathRuntimeFactory[MockRuntime]):
|
|
56
|
+
"""Runtime factory compatible with UiPathDevTerminal expectations."""
|
|
57
|
+
|
|
58
|
+
# This is the method the Textual app calls here:
|
|
59
|
+
# runtime = self.runtime_factory.new_runtime(entrypoint=run.entrypoint)
|
|
60
|
+
def new_runtime(self, entrypoint: str) -> MockRuntime:
|
|
61
|
+
return MockRuntime()
|
|
62
|
+
|
|
63
|
+
def discover_runtimes(self) -> list[MockRuntime]:
|
|
64
|
+
return []
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from uipath.core.tracing import UiPathTraceManager
|
|
2
|
+
|
|
3
|
+
from uipath.dev import UiPathDeveloperConsole
|
|
4
|
+
from uipath.dev._demo.mock_runtime import MockRuntimeFactory
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main():
|
|
8
|
+
trace_manager = UiPathTraceManager()
|
|
9
|
+
factory = MockRuntimeFactory()
|
|
10
|
+
app = UiPathDeveloperConsole(runtime_factory=factory, trace_manager=trace_manager)
|
|
11
|
+
app.run()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
if __name__ == "__main__":
|
|
15
|
+
main()
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
Screen {
|
|
2
|
+
layout: horizontal;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.run-history {
|
|
6
|
+
width: 30%;
|
|
7
|
+
min-width: 25;
|
|
8
|
+
padding-right: 1;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.run-list {
|
|
12
|
+
height: 1fr;
|
|
13
|
+
margin-bottom: 1;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.run-item {
|
|
17
|
+
padding: 0 1;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.run-running {
|
|
21
|
+
border-left: solid #ffaa00;
|
|
22
|
+
color: #ffaa00;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.run-suspended {
|
|
26
|
+
border-left: solid #00FFFF;
|
|
27
|
+
color: #e0e0e0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.run-pending {
|
|
31
|
+
border-left: solid grey;
|
|
32
|
+
color: #e0e0e0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.run-completed {
|
|
36
|
+
border-left: solid #00ff88;
|
|
37
|
+
color: #e0e0e0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.run-failed {
|
|
41
|
+
border-left: solid #ff4444;
|
|
42
|
+
color: #ff4444;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.new-run-btn {
|
|
46
|
+
width: 100%;
|
|
47
|
+
margin-bottom: 1;
|
|
48
|
+
border: none;
|
|
49
|
+
text-style: bold;
|
|
50
|
+
}
|
|
51
|
+
.main-content {
|
|
52
|
+
width: 70%;
|
|
53
|
+
padding-left: 1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.new-run-title {
|
|
57
|
+
text-style: bold;
|
|
58
|
+
padding: 0 1;
|
|
59
|
+
height: 1;
|
|
60
|
+
margin-bottom: 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.new-run-panel {
|
|
64
|
+
height: 100%;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.field-label {
|
|
68
|
+
text-style: bold;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.run-actions {
|
|
72
|
+
dock: bottom;
|
|
73
|
+
height: auto;
|
|
74
|
+
align: left middle;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.action-btn {
|
|
78
|
+
margin-right: 2;
|
|
79
|
+
min-width: 8;
|
|
80
|
+
border: none;
|
|
81
|
+
text-style: bold;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.details-content {
|
|
85
|
+
height: 1fr;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.traces-section,
|
|
89
|
+
.logs-section {
|
|
90
|
+
width: 50%;
|
|
91
|
+
height: 100%;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.traces-section {
|
|
95
|
+
width: 50%;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.logs-section {
|
|
99
|
+
width: 50%;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.detail-log {
|
|
103
|
+
height: 1fr;
|
|
104
|
+
padding: 1;
|
|
105
|
+
padding-top: 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.status-running {
|
|
109
|
+
background: #ffaa00;
|
|
110
|
+
color: #000000;
|
|
111
|
+
border: solid #ffaa00;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.status-success {
|
|
115
|
+
background: #00ff88;
|
|
116
|
+
color: #000000;
|
|
117
|
+
border: solid #00ff88;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.status-error {
|
|
121
|
+
background: #ff4444;
|
|
122
|
+
color: #ffffff;
|
|
123
|
+
border: solid #ff4444;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.hidden {
|
|
127
|
+
display: none;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
Footer {
|
|
131
|
+
margin-top:1;
|
|
132
|
+
height: auto;
|
|
133
|
+
dock: bottom;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
TabbedContent {
|
|
137
|
+
height: 100%;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
TabPane {
|
|
141
|
+
height: 100%;
|
|
142
|
+
padding: 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.traces-content {
|
|
146
|
+
height: 100%;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.spans-tree-section {
|
|
150
|
+
width: 40%;
|
|
151
|
+
height: 100%;
|
|
152
|
+
padding-right: 1;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.span-details-section {
|
|
156
|
+
width: 60%;
|
|
157
|
+
height: 100%;
|
|
158
|
+
padding-left: 1;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.spans-tree {
|
|
162
|
+
height: 100%;
|
|
163
|
+
padding: 1;
|
|
164
|
+
padding-top: 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
Label {
|
|
168
|
+
margin: 1 1;
|
|
169
|
+
width: 100%;
|
|
170
|
+
height: 100%;
|
|
171
|
+
border: tall $primary;
|
|
172
|
+
content-align: center middle;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
ContentSwitcher {
|
|
176
|
+
height: 1fr;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
SpanDetailsDisplay {
|
|
180
|
+
height: 100%;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
#span-details-display {
|
|
184
|
+
height: 100%;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
#span-details {
|
|
188
|
+
height: 100%;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.new-run-panel {
|
|
192
|
+
height: 100%;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.new-run-title {
|
|
196
|
+
text-style: bold;
|
|
197
|
+
padding: 0 1;
|
|
198
|
+
height: 2;
|
|
199
|
+
content-align: left middle;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.field-label {
|
|
203
|
+
text-style: bold;
|
|
204
|
+
margin: 1 0;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.script-input {
|
|
208
|
+
height: 3;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.json-input {
|
|
212
|
+
margin-top: 1;
|
|
213
|
+
height: auto;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.run-actions {
|
|
217
|
+
height: auto;
|
|
218
|
+
padding: 1;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.action-btn {
|
|
222
|
+
min-width: 10;
|
|
223
|
+
padding: 0 2;
|
|
224
|
+
text-style: bold;
|
|
225
|
+
border: none;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
TextArea.invalid {
|
|
229
|
+
border: tall red;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
Prompt {
|
|
234
|
+
border: wide $primary-background;
|
|
235
|
+
background: $surface;
|
|
236
|
+
color: $text;
|
|
237
|
+
margin-right: 8;
|
|
238
|
+
margin-left: 1;
|
|
239
|
+
padding: 1 1 0 1;
|
|
240
|
+
}
|
|
241
|
+
Response, Tool {
|
|
242
|
+
border: wide $primary-background;
|
|
243
|
+
background: $surface;
|
|
244
|
+
color: $text;
|
|
245
|
+
margin: 1;
|
|
246
|
+
margin-left: 8;
|
|
247
|
+
padding: 1 1 0 1;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
#chat-container{
|
|
251
|
+
background: $surface;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
#chat-input{
|
|
255
|
+
dock: bottom;
|
|
256
|
+
margin: 1;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
Checkbox{
|
|
260
|
+
margin-top: 1;
|
|
261
|
+
}
|