synapse-sdk 1.0.0a98__py3-none-any.whl → 1.0.0b1__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.
Potentially problematic release.
This version of synapse-sdk might be problematic. Click here for more details.
- synapse_sdk/cli/__init__.py +139 -84
- synapse_sdk/cli/code_server.py +169 -0
- synapse_sdk/cli/config.py +105 -4
- synapse_sdk/cli/devtools.py +54 -34
- synapse_sdk/clients/base.py +3 -4
- synapse_sdk/devtools/server.py +24 -791
- synapse_sdk/devtools/streamlit_app/__init__.py +5 -0
- synapse_sdk/devtools/streamlit_app/app.py +128 -0
- synapse_sdk/devtools/streamlit_app/services/__init__.py +11 -0
- synapse_sdk/devtools/streamlit_app/services/job_service.py +233 -0
- synapse_sdk/devtools/streamlit_app/services/plugin_service.py +236 -0
- synapse_sdk/devtools/streamlit_app/services/serve_service.py +95 -0
- synapse_sdk/devtools/streamlit_app/ui/__init__.py +15 -0
- synapse_sdk/devtools/streamlit_app/ui/config_tab.py +76 -0
- synapse_sdk/devtools/streamlit_app/ui/deployment_tab.py +66 -0
- synapse_sdk/devtools/streamlit_app/ui/http_tab.py +125 -0
- synapse_sdk/devtools/streamlit_app/ui/jobs_tab.py +573 -0
- synapse_sdk/devtools/streamlit_app/ui/serve_tab.py +346 -0
- synapse_sdk/devtools/streamlit_app/ui/status_bar.py +118 -0
- synapse_sdk/devtools/streamlit_app/utils/__init__.py +40 -0
- synapse_sdk/devtools/streamlit_app/utils/json_viewer.py +197 -0
- synapse_sdk/devtools/streamlit_app/utils/log_formatter.py +38 -0
- synapse_sdk/devtools/streamlit_app/utils/styles.py +241 -0
- synapse_sdk/devtools/streamlit_app/utils/ui_components.py +289 -0
- synapse_sdk/devtools/streamlit_app.py +10 -0
- synapse_sdk/plugins/categories/upload/actions/upload.py +2 -1
- synapse_sdk/utils/converters/dm/__init__.py +0 -1
- {synapse_sdk-1.0.0a98.dist-info → synapse_sdk-1.0.0b1.dist-info}/METADATA +4 -6
- {synapse_sdk-1.0.0a98.dist-info → synapse_sdk-1.0.0b1.dist-info}/RECORD +33 -44
- synapse_sdk/devtools/models.py +0 -55
- synapse_sdk/devtools/utils.py +0 -52
- synapse_sdk/devtools/web/.gitignore +0 -2
- synapse_sdk/devtools/web/README.md +0 -34
- synapse_sdk/devtools/web/dist/index.html +0 -17
- synapse_sdk/devtools/web/index.html +0 -16
- synapse_sdk/devtools/web/jsconfig.json +0 -15
- synapse_sdk/devtools/web/package-lock.json +0 -2609
- synapse_sdk/devtools/web/package.json +0 -27
- synapse_sdk/devtools/web/pnpm-lock.yaml +0 -1055
- synapse_sdk/devtools/web/src/App.jsx +0 -14
- synapse_sdk/devtools/web/src/App.module.css +0 -33
- synapse_sdk/devtools/web/src/assets/favicon.ico +0 -0
- synapse_sdk/devtools/web/src/components/Breadcrumbs.jsx +0 -42
- synapse_sdk/devtools/web/src/components/Layout.jsx +0 -12
- synapse_sdk/devtools/web/src/components/LogViewer.jsx +0 -280
- synapse_sdk/devtools/web/src/components/MessageViewer.jsx +0 -150
- synapse_sdk/devtools/web/src/components/NavigationSidebar.jsx +0 -128
- synapse_sdk/devtools/web/src/components/ServerStatusBar.jsx +0 -245
- synapse_sdk/devtools/web/src/components/icons.jsx +0 -325
- synapse_sdk/devtools/web/src/index.css +0 -470
- synapse_sdk/devtools/web/src/index.jsx +0 -15
- synapse_sdk/devtools/web/src/logo.svg +0 -1
- synapse_sdk/devtools/web/src/router.jsx +0 -34
- synapse_sdk/devtools/web/src/utils/api.js +0 -442
- synapse_sdk/devtools/web/src/views/ApplicationDetailView.jsx +0 -241
- synapse_sdk/devtools/web/src/views/ApplicationsView.jsx +0 -224
- synapse_sdk/devtools/web/src/views/HomeView.jsx +0 -197
- synapse_sdk/devtools/web/src/views/JobDetailView.jsx +0 -310
- synapse_sdk/devtools/web/src/views/PluginView.jsx +0 -914
- synapse_sdk/devtools/web/vite.config.js +0 -13
- {synapse_sdk-1.0.0a98.dist-info → synapse_sdk-1.0.0b1.dist-info}/WHEEL +0 -0
- {synapse_sdk-1.0.0a98.dist-info → synapse_sdk-1.0.0b1.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0a98.dist-info → synapse_sdk-1.0.0b1.dist-info}/licenses/LICENSE +0 -0
- {synapse_sdk-1.0.0a98.dist-info → synapse_sdk-1.0.0b1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""Professional JSON viewer component."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any, Dict, List, Union
|
|
5
|
+
|
|
6
|
+
import streamlit as st
|
|
7
|
+
from streamlit_ace import st_ace
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def format_json_value(value: Any, indent: int = 0) -> str:
|
|
11
|
+
"""Format a JSON value for display."""
|
|
12
|
+
if isinstance(value, (dict, list)):
|
|
13
|
+
return json.dumps(value, indent=2)
|
|
14
|
+
elif isinstance(value, str):
|
|
15
|
+
return f'"{value}"'
|
|
16
|
+
elif value is None:
|
|
17
|
+
return 'null'
|
|
18
|
+
elif isinstance(value, bool):
|
|
19
|
+
return 'true' if value else 'false'
|
|
20
|
+
else:
|
|
21
|
+
return str(value)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def render_json_compact(data: Union[Dict, List], title: str = None, expanded: bool = True):
|
|
25
|
+
"""Render JSON data in a compact, professional format."""
|
|
26
|
+
if not data:
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
if title:
|
|
30
|
+
st.markdown(f'### {title}')
|
|
31
|
+
|
|
32
|
+
# Use code editor for better JSON display
|
|
33
|
+
json_str = json.dumps(data, indent=2, ensure_ascii=False)
|
|
34
|
+
|
|
35
|
+
# Calculate appropriate height based on content
|
|
36
|
+
lines = json_str.count('\n') + 1
|
|
37
|
+
height = min(max(100, lines * 20), 600) # Min 100px, max 600px
|
|
38
|
+
|
|
39
|
+
st_ace(
|
|
40
|
+
value=json_str,
|
|
41
|
+
language='json',
|
|
42
|
+
theme='monokai',
|
|
43
|
+
key=f'json_viewer_{title}_{hash(json_str)}',
|
|
44
|
+
height=height,
|
|
45
|
+
auto_update=False,
|
|
46
|
+
font_size=13,
|
|
47
|
+
show_gutter=True,
|
|
48
|
+
show_print_margin=False,
|
|
49
|
+
wrap=False,
|
|
50
|
+
annotations=None,
|
|
51
|
+
readonly=True,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def render_json_as_table(data: Dict, title: str = None):
|
|
56
|
+
"""Render JSON data as a formatted table for key-value pairs."""
|
|
57
|
+
if not data:
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
if title:
|
|
61
|
+
st.markdown(f'### {title}')
|
|
62
|
+
|
|
63
|
+
# Create HTML table - use compact string to avoid rendering issues
|
|
64
|
+
html = ( # noqa: E501
|
|
65
|
+
'<style>'
|
|
66
|
+
'.json-table { width: 100%; border-collapse: collapse; font-family: "SF Mono", Monaco, "Cascadia Code", "Roboto Mono", monospace; font-size: 13px; margin: 10px 0; }' # noqa: E501
|
|
67
|
+
'.json-table th { background-color: #2d2d2d; color: #f0f0f0; text-align: left; padding: 10px 15px; border-bottom: 2px solid #444; font-weight: 600; }' # noqa: E501
|
|
68
|
+
'.json-table td { padding: 8px 15px; border-bottom: 1px solid #e0e0e0; vertical-align: top; }'
|
|
69
|
+
'.json-table tr:hover { background-color: #f8f9fa; }'
|
|
70
|
+
'.json-key { color: #0066cc; font-weight: 500; white-space: nowrap; width: 30%; }'
|
|
71
|
+
'.json-value { color: #333; word-break: break-word; }'
|
|
72
|
+
'.json-value-null { color: #999; font-style: italic; }'
|
|
73
|
+
'.json-value-bool { color: #d73a49; }'
|
|
74
|
+
'.json-value-number { color: #005cc5; }'
|
|
75
|
+
'.json-value-string { color: #032f62; }'
|
|
76
|
+
'.json-value-object { color: #6f42c1; font-family: "SF Mono", Monaco, monospace; font-size: 12px; background-color: #f6f8fa; padding: 4px 8px; border-radius: 3px; display: inline-block; max-width: 500px; overflow-x: auto; }' # noqa: E501
|
|
77
|
+
'</style>'
|
|
78
|
+
'<table class="json-table">'
|
|
79
|
+
'<thead><tr><th>Key</th><th>Value</th></tr></thead>'
|
|
80
|
+
'<tbody>'
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
for key, value in data.items():
|
|
84
|
+
value_class = 'json-value'
|
|
85
|
+
value_str = ''
|
|
86
|
+
|
|
87
|
+
if value is None:
|
|
88
|
+
value_class = 'json-value-null'
|
|
89
|
+
value_str = 'null'
|
|
90
|
+
elif isinstance(value, bool):
|
|
91
|
+
value_class = 'json-value-bool'
|
|
92
|
+
value_str = str(value).lower()
|
|
93
|
+
elif isinstance(value, (int, float)):
|
|
94
|
+
value_class = 'json-value-number'
|
|
95
|
+
value_str = str(value)
|
|
96
|
+
elif isinstance(value, str):
|
|
97
|
+
value_class = 'json-value-string'
|
|
98
|
+
value_str = value
|
|
99
|
+
elif isinstance(value, (dict, list)):
|
|
100
|
+
value_class = 'json-value-object'
|
|
101
|
+
value_str = json.dumps(value, ensure_ascii=False)
|
|
102
|
+
if len(value_str) > 100:
|
|
103
|
+
value_str = json.dumps(value, indent=2, ensure_ascii=False)
|
|
104
|
+
else:
|
|
105
|
+
value_str = str(value)
|
|
106
|
+
|
|
107
|
+
html += f'<tr><td class="json-key">{key}</td><td class="{value_class}">{value_str}</td></tr>'
|
|
108
|
+
|
|
109
|
+
html += '</tbody></table>'
|
|
110
|
+
|
|
111
|
+
st.markdown(html, unsafe_allow_html=True)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def render_json_tree(data: Union[Dict, List], title: str = None, max_depth: int = 3):
|
|
115
|
+
"""Render JSON data as an expandable tree view."""
|
|
116
|
+
if not data:
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
if title:
|
|
120
|
+
st.markdown(f'### {title}')
|
|
121
|
+
|
|
122
|
+
def render_node(obj: Any, key: str = '', depth: int = 0):
|
|
123
|
+
"""Recursively render JSON nodes."""
|
|
124
|
+
indent = ' ' * (depth * 20)
|
|
125
|
+
|
|
126
|
+
if isinstance(obj, dict):
|
|
127
|
+
if depth < max_depth:
|
|
128
|
+
with st.expander(f'{key or "Object"} ({len(obj)} items)', expanded=depth == 0):
|
|
129
|
+
for k, v in obj.items():
|
|
130
|
+
if isinstance(v, (dict, list)):
|
|
131
|
+
render_node(v, k, depth + 1)
|
|
132
|
+
else:
|
|
133
|
+
st.markdown(
|
|
134
|
+
f"<div style='font-family: monospace; font-size: 13px; padding: 2px 0;'>"
|
|
135
|
+
f"<span style='color: #0066cc; font-weight: 500;'>{k}:</span> "
|
|
136
|
+
f"<span style='color: #333;'>{format_json_value(v)}</span></div>",
|
|
137
|
+
unsafe_allow_html=True,
|
|
138
|
+
)
|
|
139
|
+
else:
|
|
140
|
+
st.code(json.dumps(obj, indent=2), language='json')
|
|
141
|
+
|
|
142
|
+
elif isinstance(obj, list):
|
|
143
|
+
if depth < max_depth:
|
|
144
|
+
with st.expander(f'{key or "Array"} [{len(obj)} items]', expanded=depth == 0):
|
|
145
|
+
for i, item in enumerate(obj):
|
|
146
|
+
render_node(item, f'[{i}]', depth + 1)
|
|
147
|
+
else:
|
|
148
|
+
st.code(json.dumps(obj, indent=2), language='json')
|
|
149
|
+
|
|
150
|
+
else:
|
|
151
|
+
st.markdown(
|
|
152
|
+
f"<div style='font-family: monospace; font-size: 13px; padding: 2px 0;'>"
|
|
153
|
+
f"{indent}<span style='color: #333;'>{format_json_value(obj)}</span></div>",
|
|
154
|
+
unsafe_allow_html=True,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
render_node(data)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def render_metrics_grid(metrics: Dict, title: str = 'Metrics'):
|
|
161
|
+
"""Render metrics in a professional grid layout."""
|
|
162
|
+
if not metrics:
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
st.markdown(f'### {title}')
|
|
166
|
+
|
|
167
|
+
# Create columns based on number of metrics
|
|
168
|
+
num_metrics = len(metrics)
|
|
169
|
+
if num_metrics <= 3:
|
|
170
|
+
cols = st.columns(num_metrics)
|
|
171
|
+
elif num_metrics <= 6:
|
|
172
|
+
cols = st.columns(3)
|
|
173
|
+
else:
|
|
174
|
+
cols = st.columns(4)
|
|
175
|
+
|
|
176
|
+
for i, (key, value) in enumerate(metrics.items()):
|
|
177
|
+
col_idx = i % len(cols)
|
|
178
|
+
with cols[col_idx]:
|
|
179
|
+
# Format the value
|
|
180
|
+
if isinstance(value, float):
|
|
181
|
+
if value < 1:
|
|
182
|
+
formatted_value = f'{value:.4f}'
|
|
183
|
+
else:
|
|
184
|
+
formatted_value = f'{value:.2f}'
|
|
185
|
+
elif isinstance(value, dict):
|
|
186
|
+
formatted_value = f'{len(value)} items'
|
|
187
|
+
elif isinstance(value, list):
|
|
188
|
+
formatted_value = f'{len(value)} items'
|
|
189
|
+
else:
|
|
190
|
+
formatted_value = str(value)
|
|
191
|
+
|
|
192
|
+
# Display as metric
|
|
193
|
+
st.metric(
|
|
194
|
+
label=key.replace('_', ' ').title(),
|
|
195
|
+
value=formatted_value,
|
|
196
|
+
delta=None,
|
|
197
|
+
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Log formatting utilities."""
|
|
2
|
+
|
|
3
|
+
import html
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def format_log_line(line: str, line_number: int) -> str:
|
|
8
|
+
"""Format a log line with syntax highlighting and line numbers."""
|
|
9
|
+
# Clean the line
|
|
10
|
+
line = line.strip()
|
|
11
|
+
if not line:
|
|
12
|
+
return (
|
|
13
|
+
f'<div class="log-line"><span class="log-line-number">{line_number}</span>'
|
|
14
|
+
f'<div class="log-line-content"></div></div>'
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
# Escape HTML first
|
|
18
|
+
line = html.escape(line)
|
|
19
|
+
|
|
20
|
+
# Check for different log levels and patterns
|
|
21
|
+
if re.search(r'\b(error|exception|fail|fatal)\b', line, re.IGNORECASE):
|
|
22
|
+
css_class = 'log-error'
|
|
23
|
+
elif re.search(r'\b(warn|warning)\b', line, re.IGNORECASE):
|
|
24
|
+
css_class = 'log-warning'
|
|
25
|
+
elif re.search(r'\b(info|information)\b', line, re.IGNORECASE):
|
|
26
|
+
css_class = 'log-info'
|
|
27
|
+
elif re.search(r'\b(debug|trace)\b', line, re.IGNORECASE):
|
|
28
|
+
css_class = 'log-debug'
|
|
29
|
+
else:
|
|
30
|
+
css_class = ''
|
|
31
|
+
|
|
32
|
+
# Highlight timestamps (common patterns)
|
|
33
|
+
line = re.sub(r'(\d{4}-\d{2}-\d{2}[\sT]\d{2}:\d{2}:\d{2})', r'<span class="log-timestamp">\1</span>', line)
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
f'<div class="log-line"><span class="log-line-number">{line_number}</span>'
|
|
37
|
+
f'<div class="log-line-content {css_class}">{line}</div></div>'
|
|
38
|
+
)
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""Styling constants and CSS for the Streamlit app."""
|
|
2
|
+
|
|
3
|
+
CUSTOM_CSS = """
|
|
4
|
+
<style>
|
|
5
|
+
/* Global typography and spacing */
|
|
6
|
+
* {
|
|
7
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/* Consistent button styling */
|
|
11
|
+
.stButton > button {
|
|
12
|
+
border-radius: 6px;
|
|
13
|
+
font-weight: 500;
|
|
14
|
+
transition: all 0.2s ease;
|
|
15
|
+
padding: 0.5rem 1rem;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.stButton > button:hover {
|
|
19
|
+
transform: translateY(-1px);
|
|
20
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* Tab styling */
|
|
24
|
+
.stTabs [data-baseweb="tab-list"] button [data-testid="stMarkdownContainer"] p {
|
|
25
|
+
font-size: 14px;
|
|
26
|
+
font-weight: 500;
|
|
27
|
+
letter-spacing: 0.3px;
|
|
28
|
+
}
|
|
29
|
+
.success-box {
|
|
30
|
+
padding: 1rem;
|
|
31
|
+
border-radius: 0.5rem;
|
|
32
|
+
background-color: #d4edda;
|
|
33
|
+
border: 1px solid #c3e6cb;
|
|
34
|
+
color: #155724;
|
|
35
|
+
margin: 1rem 0;
|
|
36
|
+
}
|
|
37
|
+
.error-box {
|
|
38
|
+
padding: 1rem;
|
|
39
|
+
border-radius: 0.5rem;
|
|
40
|
+
background-color: #f8d7da;
|
|
41
|
+
border: 1px solid #f5c6cb;
|
|
42
|
+
color: #721c24;
|
|
43
|
+
margin: 1rem 0;
|
|
44
|
+
}
|
|
45
|
+
.info-box {
|
|
46
|
+
padding: 1rem;
|
|
47
|
+
border-radius: 0.5rem;
|
|
48
|
+
background-color: #d1ecf1;
|
|
49
|
+
border: 1px solid #bee5eb;
|
|
50
|
+
color: #0c5460;
|
|
51
|
+
margin: 1rem 0;
|
|
52
|
+
}
|
|
53
|
+
.status-bar {
|
|
54
|
+
background-color: #f8f9fa;
|
|
55
|
+
border-radius: 0.5rem;
|
|
56
|
+
padding: 1rem;
|
|
57
|
+
margin: 1rem 0;
|
|
58
|
+
border: 1px solid #dee2e6;
|
|
59
|
+
}
|
|
60
|
+
.log-container {
|
|
61
|
+
background-color: #1e1e1e;
|
|
62
|
+
color: #d4d4d4;
|
|
63
|
+
font-family: 'Fira Code', 'Consolas', 'Monaco', 'Courier New', monospace;
|
|
64
|
+
font-size: 14px;
|
|
65
|
+
line-height: 1.4;
|
|
66
|
+
padding: 0;
|
|
67
|
+
border-radius: 0.5rem;
|
|
68
|
+
overflow-x: auto;
|
|
69
|
+
overflow-y: auto;
|
|
70
|
+
max-height: 400px;
|
|
71
|
+
white-space: pre-wrap;
|
|
72
|
+
word-wrap: break-word;
|
|
73
|
+
border: 1px solid #3c3c3c;
|
|
74
|
+
display: flex;
|
|
75
|
+
flex-direction: column;
|
|
76
|
+
}
|
|
77
|
+
.log-line {
|
|
78
|
+
margin: 0;
|
|
79
|
+
padding: 0.2rem 1rem;
|
|
80
|
+
display: flex;
|
|
81
|
+
align-items: flex-start;
|
|
82
|
+
border-bottom: 1px solid #2a2a2a;
|
|
83
|
+
}
|
|
84
|
+
.log-line:hover {
|
|
85
|
+
background-color: #2a2a2a;
|
|
86
|
+
}
|
|
87
|
+
.log-line-number {
|
|
88
|
+
color: #858585;
|
|
89
|
+
font-size: 12px;
|
|
90
|
+
min-width: 40px;
|
|
91
|
+
text-align: right;
|
|
92
|
+
padding-right: 12px;
|
|
93
|
+
user-select: none;
|
|
94
|
+
flex-shrink: 0;
|
|
95
|
+
}
|
|
96
|
+
.log-line-content {
|
|
97
|
+
flex: 1;
|
|
98
|
+
word-break: break-word;
|
|
99
|
+
}
|
|
100
|
+
.log-error {
|
|
101
|
+
color: #f48771;
|
|
102
|
+
font-weight: bold;
|
|
103
|
+
}
|
|
104
|
+
.log-warning {
|
|
105
|
+
color: #dcdcaa;
|
|
106
|
+
}
|
|
107
|
+
.log-info {
|
|
108
|
+
color: #9cdcfe;
|
|
109
|
+
}
|
|
110
|
+
.log-debug {
|
|
111
|
+
color: #608b4e;
|
|
112
|
+
}
|
|
113
|
+
.log-timestamp {
|
|
114
|
+
color: #569cd6;
|
|
115
|
+
}
|
|
116
|
+
.stJson {
|
|
117
|
+
max-width: 100%;
|
|
118
|
+
overflow-x: auto;
|
|
119
|
+
}
|
|
120
|
+
/* Professional styling for code blocks */
|
|
121
|
+
.stCodeBlock {
|
|
122
|
+
background-color: #1e1e1e !important;
|
|
123
|
+
border: 1px solid #3c3c3c !important;
|
|
124
|
+
border-radius: 6px !important;
|
|
125
|
+
}
|
|
126
|
+
/* Enhanced headers */
|
|
127
|
+
h1, h2, h3 {
|
|
128
|
+
font-weight: 600 !important;
|
|
129
|
+
color: #1a1a1a !important;
|
|
130
|
+
}
|
|
131
|
+
/* Professional section spacing */
|
|
132
|
+
.section-divider {
|
|
133
|
+
margin: 2rem 0;
|
|
134
|
+
border-bottom: 1px solid #e0e0e0;
|
|
135
|
+
}
|
|
136
|
+
/* Hide the Streamlit toolbar */
|
|
137
|
+
.stAppToolbar {
|
|
138
|
+
visibility: hidden;
|
|
139
|
+
}
|
|
140
|
+
/* Alternative method to hide toolbar */
|
|
141
|
+
[data-testid="stToolbar"] {
|
|
142
|
+
display: none !important;
|
|
143
|
+
}
|
|
144
|
+
/* Reduce top padding of main container */
|
|
145
|
+
.stMainBlockContainer,
|
|
146
|
+
.block-container {
|
|
147
|
+
padding-top: 1rem !important;
|
|
148
|
+
max-width: 100%;
|
|
149
|
+
}
|
|
150
|
+
/* Better tab styling */
|
|
151
|
+
.stTabs [data-baseweb="tab-list"] {
|
|
152
|
+
gap: 4px;
|
|
153
|
+
background-color: transparent;
|
|
154
|
+
border-bottom: 2px solid #e9ecef;
|
|
155
|
+
padding: 0;
|
|
156
|
+
}
|
|
157
|
+
.stTabs [data-baseweb="tab"] {
|
|
158
|
+
padding: 10px 20px;
|
|
159
|
+
border-radius: 0;
|
|
160
|
+
background-color: transparent;
|
|
161
|
+
border-bottom: 3px solid transparent;
|
|
162
|
+
margin-bottom: -2px;
|
|
163
|
+
}
|
|
164
|
+
.stTabs [data-baseweb="tab-list"] button[aria-selected="true"] {
|
|
165
|
+
background-color: transparent;
|
|
166
|
+
border-bottom: 3px solid #007bff;
|
|
167
|
+
color: #007bff;
|
|
168
|
+
}
|
|
169
|
+
.stTabs [data-baseweb="tab"]:hover {
|
|
170
|
+
background-color: #f8f9fa;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* Form styling */
|
|
174
|
+
.stSelectbox > div > div {
|
|
175
|
+
border-radius: 6px;
|
|
176
|
+
}
|
|
177
|
+
.stTextInput > div > div > input {
|
|
178
|
+
border-radius: 6px;
|
|
179
|
+
}
|
|
180
|
+
.stTextArea > div > div > textarea {
|
|
181
|
+
border-radius: 6px;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* Checkbox and radio styling */
|
|
185
|
+
.stCheckbox > label > span {
|
|
186
|
+
font-size: 14px;
|
|
187
|
+
}
|
|
188
|
+
.stRadio > label {
|
|
189
|
+
font-size: 14px;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* Metrics styling */
|
|
193
|
+
.metric-container .metric-label {
|
|
194
|
+
font-size: 12px;
|
|
195
|
+
font-weight: 500;
|
|
196
|
+
text-transform: uppercase;
|
|
197
|
+
letter-spacing: 0.5px;
|
|
198
|
+
color: #6c757d;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* Consistent spacing */
|
|
202
|
+
.row-widget.stButton {
|
|
203
|
+
margin-top: 0.5rem;
|
|
204
|
+
margin-bottom: 0.5rem;
|
|
205
|
+
}
|
|
206
|
+
/* Also adjust the header spacing */
|
|
207
|
+
.stAppHeader {
|
|
208
|
+
height: 0rem !important;
|
|
209
|
+
}
|
|
210
|
+
</style>
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def get_status_badge_html(status: str) -> str:
|
|
215
|
+
"""Generate HTML for status badge with outline style."""
|
|
216
|
+
status = status.upper() if status else 'UNKNOWN'
|
|
217
|
+
|
|
218
|
+
color_map = {
|
|
219
|
+
'SUCCEEDED': '#28a745',
|
|
220
|
+
'RUNNING': '#007bff',
|
|
221
|
+
'PENDING': '#6c757d',
|
|
222
|
+
'FAILED': '#dc3545',
|
|
223
|
+
'STOPPED': '#6c757d',
|
|
224
|
+
'UNKNOWN': '#6c757d',
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
color = color_map.get(status, '#6c757d')
|
|
228
|
+
|
|
229
|
+
return f"""
|
|
230
|
+
<span style="
|
|
231
|
+
border: 1px solid {color};
|
|
232
|
+
color: {color};
|
|
233
|
+
padding: 4px 10px;
|
|
234
|
+
border-radius: 4px;
|
|
235
|
+
font-size: 12px;
|
|
236
|
+
font-weight: 500;
|
|
237
|
+
display: inline-block;
|
|
238
|
+
text-align: center;
|
|
239
|
+
min-width: 70px;
|
|
240
|
+
">{status}</span>
|
|
241
|
+
"""
|