uipath 2.1.26__py3-none-any.whl → 2.1.28__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/_cli/__init__.py +2 -0
- uipath/_cli/_dev/_terminal/__init__.py +227 -0
- uipath/_cli/_dev/_terminal/_components/_details.py +421 -0
- uipath/_cli/_dev/_terminal/_components/_history.py +57 -0
- uipath/_cli/_dev/_terminal/_components/_new.py +133 -0
- uipath/_cli/_dev/_terminal/_models/_execution.py +43 -0
- uipath/_cli/_dev/_terminal/_models/_messages.py +65 -0
- uipath/_cli/_dev/_terminal/_styles/terminal.tcss +361 -0
- uipath/_cli/_dev/_terminal/_traces/_exporter.py +119 -0
- uipath/_cli/_dev/_terminal/_traces/_logger.py +32 -0
- uipath/_cli/_runtime/_contracts.py +128 -3
- uipath/_cli/_runtime/_logging.py +20 -13
- uipath/_cli/_runtime/_runtime.py +2 -14
- uipath/_cli/cli_dev.py +44 -0
- uipath/_cli/cli_run.py +33 -28
- uipath/_cli/middlewares.py +1 -0
- uipath/telemetry/_track.py +2 -2
- {uipath-2.1.26.dist-info → uipath-2.1.28.dist-info}/METADATA +3 -1
- {uipath-2.1.26.dist-info → uipath-2.1.28.dist-info}/RECORD +22 -12
- {uipath-2.1.26.dist-info → uipath-2.1.28.dist-info}/WHEEL +0 -0
- {uipath-2.1.26.dist-info → uipath-2.1.28.dist-info}/entry_points.txt +0 -0
- {uipath-2.1.26.dist-info → uipath-2.1.28.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
from typing import List, Optional
|
2
|
+
|
3
|
+
from textual.app import ComposeResult
|
4
|
+
from textual.containers import Container, Vertical
|
5
|
+
from textual.widgets import Button, ListItem, ListView, Static
|
6
|
+
|
7
|
+
from .._models._execution import ExecutionRun
|
8
|
+
|
9
|
+
|
10
|
+
class RunHistoryPanel(Container):
|
11
|
+
"""Left panel showing execution run history."""
|
12
|
+
|
13
|
+
def __init__(self, **kwargs):
|
14
|
+
super().__init__(**kwargs)
|
15
|
+
self.runs: List[ExecutionRun] = []
|
16
|
+
self.selected_run: Optional[ExecutionRun] = None
|
17
|
+
|
18
|
+
def compose(self) -> ComposeResult:
|
19
|
+
with Vertical():
|
20
|
+
yield ListView(id="run-list", classes="run-list")
|
21
|
+
yield Button(
|
22
|
+
"+ New Run", id="new-run-btn", variant="primary", classes="new-run-btn"
|
23
|
+
)
|
24
|
+
|
25
|
+
def add_run(self, run: ExecutionRun):
|
26
|
+
"""Add a new run to history."""
|
27
|
+
self.runs.insert(0, run) # Add to top
|
28
|
+
self.refresh_list()
|
29
|
+
|
30
|
+
def update_run(self, run: ExecutionRun):
|
31
|
+
"""Update an existing run."""
|
32
|
+
self.refresh_list()
|
33
|
+
|
34
|
+
def refresh_list(self):
|
35
|
+
"""Refresh the run list display."""
|
36
|
+
run_list = self.query_one("#run-list", ListView)
|
37
|
+
run_list.clear()
|
38
|
+
|
39
|
+
for run in self.runs:
|
40
|
+
item = ListItem(
|
41
|
+
Static(run.display_name), classes=f"run-item run-{run.status}"
|
42
|
+
)
|
43
|
+
# Store run id directly on the ListItem
|
44
|
+
item.run_id = run.id # type: ignore[attr-defined]
|
45
|
+
run_list.append(item)
|
46
|
+
|
47
|
+
def get_run_by_id(self, run_id: str) -> Optional[ExecutionRun]:
|
48
|
+
"""Get run by id."""
|
49
|
+
for run in self.runs:
|
50
|
+
if run.id == run_id:
|
51
|
+
return run
|
52
|
+
return None
|
53
|
+
|
54
|
+
def clear_runs(self):
|
55
|
+
"""Clear all runs from history."""
|
56
|
+
self.runs.clear()
|
57
|
+
self.refresh_list()
|
@@ -0,0 +1,133 @@
|
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
from typing import Any, Dict, Tuple, cast
|
4
|
+
|
5
|
+
from textual.app import ComposeResult
|
6
|
+
from textual.containers import Container, Horizontal, Vertical
|
7
|
+
from textual.reactive import reactive
|
8
|
+
from textual.widgets import Button, Select, TextArea
|
9
|
+
|
10
|
+
|
11
|
+
def mock_json_from_schema(schema: Dict[str, Any]) -> Dict[str, Any]:
|
12
|
+
props = schema.get("properties", {})
|
13
|
+
required = schema.get("required", [])
|
14
|
+
mock = {}
|
15
|
+
for key, info in props.items():
|
16
|
+
if "default" in info:
|
17
|
+
mock[key] = info["default"]
|
18
|
+
continue
|
19
|
+
t = info.get("type")
|
20
|
+
if t == "string":
|
21
|
+
mock[key] = f"example_{key}" if key in required else ""
|
22
|
+
elif t == "integer":
|
23
|
+
mock[key] = 0 if key in required else None
|
24
|
+
elif t == "boolean":
|
25
|
+
mock[key] = True if key in required else False
|
26
|
+
elif t == "array":
|
27
|
+
item_schema = info.get("items", {"type": "string"})
|
28
|
+
mock[key] = [mock_json_from_schema(item_schema)]
|
29
|
+
elif t == "object":
|
30
|
+
mock[key] = mock_json_from_schema(info)
|
31
|
+
else:
|
32
|
+
mock[key] = None
|
33
|
+
return mock
|
34
|
+
|
35
|
+
|
36
|
+
class NewRunPanel(Container):
|
37
|
+
"""Panel for creating new runs with a Select entrypoint selector."""
|
38
|
+
|
39
|
+
selected_entrypoint = reactive("")
|
40
|
+
|
41
|
+
def __init__(self, **kwargs):
|
42
|
+
super().__init__(**kwargs)
|
43
|
+
json_path = os.path.join(os.getcwd(), "uipath.json")
|
44
|
+
with open(json_path, "r") as f:
|
45
|
+
data = json.load(f)
|
46
|
+
|
47
|
+
self.entrypoints = data.get("entryPoints", [])
|
48
|
+
self.entrypoint_paths = [ep["filePath"] for ep in self.entrypoints]
|
49
|
+
|
50
|
+
self.selected_entrypoint = (
|
51
|
+
self.entrypoint_paths[0] if self.entrypoint_paths else ""
|
52
|
+
)
|
53
|
+
ep: Dict[str, Any] = next(
|
54
|
+
(
|
55
|
+
ep
|
56
|
+
for ep in self.entrypoints
|
57
|
+
if ep["filePath"] == self.selected_entrypoint
|
58
|
+
),
|
59
|
+
{},
|
60
|
+
)
|
61
|
+
self.initial_input = json.dumps(
|
62
|
+
mock_json_from_schema(ep.get("input", {})), indent=2
|
63
|
+
)
|
64
|
+
|
65
|
+
def compose(self) -> ComposeResult:
|
66
|
+
with Vertical():
|
67
|
+
options = [(path, path) for path in self.entrypoint_paths]
|
68
|
+
yield Select(
|
69
|
+
options,
|
70
|
+
id="entrypoint-select",
|
71
|
+
value=self.selected_entrypoint,
|
72
|
+
allow_blank=False,
|
73
|
+
)
|
74
|
+
|
75
|
+
yield TextArea(
|
76
|
+
text=self.initial_input,
|
77
|
+
language="json",
|
78
|
+
id="json-input",
|
79
|
+
classes="input-field json-input",
|
80
|
+
)
|
81
|
+
|
82
|
+
with Horizontal(classes="run-actions"):
|
83
|
+
yield Button(
|
84
|
+
"▶ Run", id="execute-btn", variant="primary", classes="action-btn"
|
85
|
+
)
|
86
|
+
yield Button(
|
87
|
+
"Cancel",
|
88
|
+
id="cancel-btn",
|
89
|
+
variant="default",
|
90
|
+
classes="action-btn cancel-btn",
|
91
|
+
)
|
92
|
+
|
93
|
+
async def on_select_changed(self, event: Select.Changed) -> None:
|
94
|
+
"""Update JSON input when user selects an entrypoint."""
|
95
|
+
self.selected_entrypoint = cast(str, event.value)
|
96
|
+
|
97
|
+
ep: Dict[str, Any] = next(
|
98
|
+
(
|
99
|
+
ep
|
100
|
+
for ep in self.entrypoints
|
101
|
+
if ep["filePath"] == self.selected_entrypoint
|
102
|
+
),
|
103
|
+
{},
|
104
|
+
)
|
105
|
+
json_input = self.query_one("#json-input", TextArea)
|
106
|
+
json_input.text = json.dumps(
|
107
|
+
mock_json_from_schema(ep.get("input", {})), indent=2
|
108
|
+
)
|
109
|
+
|
110
|
+
def get_input_values(self) -> Tuple[str, str]:
|
111
|
+
json_input = self.query_one("#json-input", TextArea)
|
112
|
+
return self.selected_entrypoint, json_input.text.strip()
|
113
|
+
|
114
|
+
def reset_form(self):
|
115
|
+
"""Reset selection and JSON input to defaults."""
|
116
|
+
self.selected_entrypoint = (
|
117
|
+
self.entrypoint_paths[0] if self.entrypoint_paths else ""
|
118
|
+
)
|
119
|
+
select = self.query_one("#entrypoint-select", Select)
|
120
|
+
select.value = self.selected_entrypoint
|
121
|
+
|
122
|
+
ep: Dict[str, Any] = next(
|
123
|
+
(
|
124
|
+
ep
|
125
|
+
for ep in self.entrypoints
|
126
|
+
if ep["filePath"] == self.selected_entrypoint
|
127
|
+
),
|
128
|
+
{},
|
129
|
+
)
|
130
|
+
json_input = self.query_one("#json-input", TextArea)
|
131
|
+
json_input.text = json.dumps(
|
132
|
+
mock_json_from_schema(ep.get("input", {})), indent=2
|
133
|
+
)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import os
|
2
|
+
from datetime import datetime
|
3
|
+
from typing import List, Optional
|
4
|
+
from uuid import uuid4
|
5
|
+
|
6
|
+
from ._messages import LogMessage, TraceMessage
|
7
|
+
|
8
|
+
|
9
|
+
class ExecutionRun:
|
10
|
+
"""Represents a single execution run."""
|
11
|
+
|
12
|
+
def __init__(self, entrypoint: str, input_data: str):
|
13
|
+
self.id = str(uuid4())[:8]
|
14
|
+
self.entrypoint = entrypoint
|
15
|
+
self.input_data = input_data
|
16
|
+
self.output_data: Optional[str] = None
|
17
|
+
self.start_time = datetime.now()
|
18
|
+
self.end_time: Optional[datetime] = None
|
19
|
+
self.status = "running" # running, completed, failed
|
20
|
+
self.traces: List[TraceMessage] = []
|
21
|
+
self.logs: List[LogMessage] = []
|
22
|
+
|
23
|
+
@property
|
24
|
+
def duration(self) -> str:
|
25
|
+
if self.end_time:
|
26
|
+
delta = self.end_time - self.start_time
|
27
|
+
return f"{delta.total_seconds():.1f}s"
|
28
|
+
else:
|
29
|
+
delta = datetime.now() - self.start_time
|
30
|
+
return f"{delta.total_seconds():.1f}s"
|
31
|
+
|
32
|
+
@property
|
33
|
+
def display_name(self) -> str:
|
34
|
+
status_icon = {"running": "⚙️", "completed": "✅", "failed": "❌"}.get(
|
35
|
+
self.status, "❓"
|
36
|
+
)
|
37
|
+
|
38
|
+
script_name = (
|
39
|
+
os.path.basename(self.entrypoint) if self.entrypoint else "untitled"
|
40
|
+
)
|
41
|
+
time_str = self.start_time.strftime("%H:%M:%S")
|
42
|
+
|
43
|
+
return f"{status_icon} {script_name} ({time_str}) [{self.duration}]"
|
@@ -0,0 +1,65 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
from typing import Any, Dict, Optional
|
3
|
+
|
4
|
+
from textual.message import Message
|
5
|
+
|
6
|
+
|
7
|
+
class ExecutionMessage(Message):
|
8
|
+
"""Message sent when execution starts or completes."""
|
9
|
+
|
10
|
+
def __init__(
|
11
|
+
self,
|
12
|
+
run_id: str,
|
13
|
+
status: str,
|
14
|
+
success: Optional[bool] = None,
|
15
|
+
error: Optional[str] = None,
|
16
|
+
):
|
17
|
+
self.run_id = run_id
|
18
|
+
self.status = status # "started", "completed", "failed"
|
19
|
+
self.success = success
|
20
|
+
self.error = error
|
21
|
+
super().__init__()
|
22
|
+
|
23
|
+
|
24
|
+
class LogMessage(Message):
|
25
|
+
"""Message sent when a new log entry is created."""
|
26
|
+
|
27
|
+
def __init__(
|
28
|
+
self,
|
29
|
+
run_id: str,
|
30
|
+
level: str,
|
31
|
+
message: str,
|
32
|
+
timestamp: Optional[datetime] = None,
|
33
|
+
):
|
34
|
+
self.run_id = run_id
|
35
|
+
self.level = level
|
36
|
+
self.message = message
|
37
|
+
self.timestamp = timestamp or datetime.now()
|
38
|
+
super().__init__()
|
39
|
+
|
40
|
+
|
41
|
+
class TraceMessage(Message):
|
42
|
+
"""Message sent when a new trace entry is created."""
|
43
|
+
|
44
|
+
def __init__(
|
45
|
+
self,
|
46
|
+
run_id: str,
|
47
|
+
span_name: str,
|
48
|
+
span_id: str,
|
49
|
+
parent_span_id: Optional[str] = None,
|
50
|
+
trace_id: Optional[str] = None,
|
51
|
+
status: str = "running",
|
52
|
+
duration_ms: Optional[float] = None,
|
53
|
+
timestamp: Optional[datetime] = None,
|
54
|
+
attributes: Optional[Dict[str, Any]] = None,
|
55
|
+
):
|
56
|
+
self.run_id = run_id
|
57
|
+
self.span_name = span_name
|
58
|
+
self.span_id = span_id
|
59
|
+
self.parent_span_id = parent_span_id
|
60
|
+
self.trace_id = trace_id
|
61
|
+
self.status = status
|
62
|
+
self.duration_ms = duration_ms
|
63
|
+
self.timestamp = timestamp or datetime.now()
|
64
|
+
self.attributes = attributes or {}
|
65
|
+
super().__init__()
|
@@ -0,0 +1,361 @@
|
|
1
|
+
/* Global layout - dark terminal theme */
|
2
|
+
Screen {
|
3
|
+
background: #1a1a1a;
|
4
|
+
color: #e0e0e0;
|
5
|
+
layout: horizontal;
|
6
|
+
}
|
7
|
+
|
8
|
+
/* Left sidebar - run history with embedded title */
|
9
|
+
.run-history {
|
10
|
+
width: 30%;
|
11
|
+
min-width: 25;
|
12
|
+
padding-right: 1;
|
13
|
+
}
|
14
|
+
|
15
|
+
.run-list {
|
16
|
+
height: 1fr;
|
17
|
+
margin-bottom: 1;
|
18
|
+
border: solid #404040;
|
19
|
+
background: #252525;
|
20
|
+
}
|
21
|
+
|
22
|
+
.run-item {
|
23
|
+
padding: 0 1;
|
24
|
+
background: transparent;
|
25
|
+
color: #e0e0e0;
|
26
|
+
}
|
27
|
+
|
28
|
+
.run-item:hover {
|
29
|
+
background: #333333;
|
30
|
+
}
|
31
|
+
|
32
|
+
.run-running {
|
33
|
+
border-left: solid #ffaa00;
|
34
|
+
color: #ffaa00;
|
35
|
+
}
|
36
|
+
|
37
|
+
.run-completed {
|
38
|
+
border-left: solid #00ff88;
|
39
|
+
color: #e0e0e0;
|
40
|
+
}
|
41
|
+
|
42
|
+
.run-failed {
|
43
|
+
border-left: solid #ff4444;
|
44
|
+
color: #ff4444;
|
45
|
+
}
|
46
|
+
|
47
|
+
.new-run-btn {
|
48
|
+
width: 100%;
|
49
|
+
margin-bottom: 1;
|
50
|
+
background: #00d4ff;
|
51
|
+
color: #000000;
|
52
|
+
border: none;
|
53
|
+
text-style: bold;
|
54
|
+
}
|
55
|
+
|
56
|
+
.new-run-btn:hover {
|
57
|
+
background: #00a0cc;
|
58
|
+
}
|
59
|
+
|
60
|
+
/* Main content area */
|
61
|
+
.main-content {
|
62
|
+
width: 70%;
|
63
|
+
padding-left: 1;
|
64
|
+
}
|
65
|
+
|
66
|
+
/* New run panel with embedded titles */
|
67
|
+
.new-run-title {
|
68
|
+
color: #00d4ff;
|
69
|
+
text-style: bold;
|
70
|
+
background: #252525;
|
71
|
+
padding: 0 1;
|
72
|
+
height: 1;
|
73
|
+
border: solid #404040;
|
74
|
+
margin-bottom: 0;
|
75
|
+
}
|
76
|
+
|
77
|
+
.new-run-panel {
|
78
|
+
height: 100%;
|
79
|
+
background: #252525;
|
80
|
+
border: solid #404040;
|
81
|
+
}
|
82
|
+
|
83
|
+
.field-label {
|
84
|
+
color: #00d4ff;
|
85
|
+
text-style: bold;
|
86
|
+
background: #252525;
|
87
|
+
}
|
88
|
+
|
89
|
+
.run-actions {
|
90
|
+
height: 2;
|
91
|
+
align: left middle;
|
92
|
+
}
|
93
|
+
|
94
|
+
.action-btn {
|
95
|
+
margin-right: 2;
|
96
|
+
min-width: 8;
|
97
|
+
background: #00ff88;
|
98
|
+
color: #000000;
|
99
|
+
border: none;
|
100
|
+
text-style: bold;
|
101
|
+
}
|
102
|
+
|
103
|
+
.action-btn:hover {
|
104
|
+
background: #00cc66;
|
105
|
+
}
|
106
|
+
|
107
|
+
.cancel-btn {
|
108
|
+
background: #404040;
|
109
|
+
color: #e0e0e0;
|
110
|
+
}
|
111
|
+
|
112
|
+
.cancel-btn:hover {
|
113
|
+
background: #555555;
|
114
|
+
}
|
115
|
+
|
116
|
+
/* Run details panel with embedded titles */
|
117
|
+
.details-content {
|
118
|
+
height: 1fr;
|
119
|
+
}
|
120
|
+
|
121
|
+
.traces-section,
|
122
|
+
.logs-section {
|
123
|
+
width: 50%;
|
124
|
+
height: 100%;
|
125
|
+
}
|
126
|
+
|
127
|
+
.traces-section {
|
128
|
+
width: 50%;
|
129
|
+
}
|
130
|
+
|
131
|
+
.logs-section {
|
132
|
+
width: 50%;
|
133
|
+
}
|
134
|
+
|
135
|
+
.detail-log {
|
136
|
+
height: 1fr;
|
137
|
+
background: #252525;
|
138
|
+
padding: 1;
|
139
|
+
padding-top: 0;
|
140
|
+
}
|
141
|
+
|
142
|
+
/* Status indicators for buttons */
|
143
|
+
.status-running {
|
144
|
+
background: #ffaa00;
|
145
|
+
color: #000000;
|
146
|
+
border: solid #ffaa00;
|
147
|
+
}
|
148
|
+
|
149
|
+
.status-success {
|
150
|
+
background: #00ff88;
|
151
|
+
color: #000000;
|
152
|
+
border: solid #00ff88;
|
153
|
+
}
|
154
|
+
|
155
|
+
.status-error {
|
156
|
+
background: #ff4444;
|
157
|
+
color: #ffffff;
|
158
|
+
border: solid #ff4444;
|
159
|
+
}
|
160
|
+
|
161
|
+
/* ListView styling */
|
162
|
+
ListView {
|
163
|
+
background: transparent;
|
164
|
+
}
|
165
|
+
|
166
|
+
ListView>ListItem {
|
167
|
+
background: transparent;
|
168
|
+
}
|
169
|
+
|
170
|
+
ListView>ListItem:hover {
|
171
|
+
background: #333333;
|
172
|
+
}
|
173
|
+
|
174
|
+
ListView>ListItem.--highlight {
|
175
|
+
background: #00d4ff;
|
176
|
+
color: #000000;
|
177
|
+
}
|
178
|
+
|
179
|
+
/* TextArea and RichLog improvements */
|
180
|
+
TextArea {
|
181
|
+
background: #1a1a1a;
|
182
|
+
color: #e0e0e0;
|
183
|
+
}
|
184
|
+
|
185
|
+
RichLog {
|
186
|
+
background: #252525;
|
187
|
+
color: #e0e0e0;
|
188
|
+
}
|
189
|
+
|
190
|
+
/* Initially hide details panel */
|
191
|
+
.hidden {
|
192
|
+
display: none;
|
193
|
+
}
|
194
|
+
|
195
|
+
/* Header and Footer */
|
196
|
+
Header {
|
197
|
+
background: #000000;
|
198
|
+
color: #00d4ff;
|
199
|
+
}
|
200
|
+
|
201
|
+
Footer {
|
202
|
+
background: #000000;
|
203
|
+
color: #888888;
|
204
|
+
border-top: solid #404040;
|
205
|
+
}
|
206
|
+
|
207
|
+
/* Tabbed content styling */
|
208
|
+
TabbedContent {
|
209
|
+
height: 100%;
|
210
|
+
}
|
211
|
+
|
212
|
+
TabPane {
|
213
|
+
height: 100%;
|
214
|
+
padding: 0;
|
215
|
+
}
|
216
|
+
|
217
|
+
/* Traces tab layout */
|
218
|
+
.traces-content {
|
219
|
+
height: 100%;
|
220
|
+
}
|
221
|
+
|
222
|
+
.spans-tree-section {
|
223
|
+
width: 40%;
|
224
|
+
height: 100%;
|
225
|
+
padding-right: 1;
|
226
|
+
}
|
227
|
+
|
228
|
+
.span-details-section {
|
229
|
+
width: 60%;
|
230
|
+
height: 100%;
|
231
|
+
padding-left: 1;
|
232
|
+
}
|
233
|
+
|
234
|
+
/* Tree styling */
|
235
|
+
.spans-tree {
|
236
|
+
height: 100%;
|
237
|
+
background: #252525;
|
238
|
+
padding: 1;
|
239
|
+
padding-top: 0;
|
240
|
+
}
|
241
|
+
|
242
|
+
Tree {
|
243
|
+
background: #252525;
|
244
|
+
color: #e0e0e0;
|
245
|
+
}
|
246
|
+
|
247
|
+
Tree>TreeNode {
|
248
|
+
background: transparent;
|
249
|
+
}
|
250
|
+
|
251
|
+
Tree>TreeNode:hover {
|
252
|
+
background: #333333;
|
253
|
+
}
|
254
|
+
|
255
|
+
Tree>TreeNode.--highlight {
|
256
|
+
background: #00d4ff;
|
257
|
+
color: #000000;
|
258
|
+
}
|
259
|
+
|
260
|
+
/* Tab styling */
|
261
|
+
Tabs {
|
262
|
+
background: #252525;
|
263
|
+
color: #e0e0e0;
|
264
|
+
}
|
265
|
+
|
266
|
+
Tab {
|
267
|
+
background: transparent;
|
268
|
+
color: #888888;
|
269
|
+
border: none;
|
270
|
+
}
|
271
|
+
|
272
|
+
Label {
|
273
|
+
margin: 1 1;
|
274
|
+
width: 100%;
|
275
|
+
height: 100%;
|
276
|
+
background: $panel;
|
277
|
+
border: tall $primary;
|
278
|
+
content-align: center middle;
|
279
|
+
}
|
280
|
+
|
281
|
+
/* Content areas */
|
282
|
+
ContentSwitcher {
|
283
|
+
height: 1fr;
|
284
|
+
background: transparent;
|
285
|
+
}
|
286
|
+
|
287
|
+
/* Span details styling */
|
288
|
+
SpanDetailsDisplay {
|
289
|
+
height: 100%;
|
290
|
+
}
|
291
|
+
|
292
|
+
#span-details-display {
|
293
|
+
height: 100%;
|
294
|
+
}
|
295
|
+
|
296
|
+
#span-details {
|
297
|
+
height: 100%;
|
298
|
+
}
|
299
|
+
|
300
|
+
/* Panels */
|
301
|
+
.new-run-panel {
|
302
|
+
height: 100%;
|
303
|
+
background: #252525;
|
304
|
+
padding: 1;
|
305
|
+
}
|
306
|
+
|
307
|
+
/* Section Titles */
|
308
|
+
.new-run-title {
|
309
|
+
color: #00d4ff;
|
310
|
+
text-style: bold;
|
311
|
+
background: #1f1f1f;
|
312
|
+
padding: 0 1;
|
313
|
+
height: 2;
|
314
|
+
content-align: left middle;
|
315
|
+
}
|
316
|
+
|
317
|
+
/* Labels */
|
318
|
+
.field-label {
|
319
|
+
color: #00d4ff;
|
320
|
+
text-style: bold;
|
321
|
+
margin: 1 0;
|
322
|
+
}
|
323
|
+
|
324
|
+
.input-field {
|
325
|
+
background: #1a1a1a;
|
326
|
+
color: #e0e0e0;
|
327
|
+
}
|
328
|
+
|
329
|
+
.script-input {
|
330
|
+
height: 3;
|
331
|
+
}
|
332
|
+
|
333
|
+
.json-input {
|
334
|
+
margin-top: 1;
|
335
|
+
height: 12;
|
336
|
+
}
|
337
|
+
|
338
|
+
.run-actions {
|
339
|
+
height: auto;
|
340
|
+
padding: 1;
|
341
|
+
}
|
342
|
+
|
343
|
+
.action-btn {
|
344
|
+
min-width: 10;
|
345
|
+
padding: 0 2;
|
346
|
+
text-style: bold;
|
347
|
+
border: none;
|
348
|
+
}
|
349
|
+
|
350
|
+
.action-btn:hover {
|
351
|
+
background: #00cc66;
|
352
|
+
}
|
353
|
+
|
354
|
+
.cancel-btn {
|
355
|
+
background: #404040;
|
356
|
+
color: #e0e0e0;
|
357
|
+
}
|
358
|
+
|
359
|
+
.cancel-btn:hover {
|
360
|
+
background: #555555;
|
361
|
+
}
|