synapse-sdk 1.0.0a98__py3-none-any.whl → 1.0.0b2__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/coco/from_dm.py +2 -2
- synapse_sdk/utils/converters/dm/__init__.py +0 -1
- {synapse_sdk-1.0.0a98.dist-info → synapse_sdk-1.0.0b2.dist-info}/METADATA +4 -6
- {synapse_sdk-1.0.0a98.dist-info → synapse_sdk-1.0.0b2.dist-info}/RECORD +34 -45
- 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.0b2.dist-info}/WHEEL +0 -0
- {synapse_sdk-1.0.0a98.dist-info → synapse_sdk-1.0.0b2.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0a98.dist-info → synapse_sdk-1.0.0b2.dist-info}/licenses/LICENSE +0 -0
- {synapse_sdk-1.0.0a98.dist-info → synapse_sdk-1.0.0b2.dist-info}/top_level.txt +0 -0
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { AppRouter } from "./router";
|
|
2
|
-
import ServerStatusBar from "./components/ServerStatusBar";
|
|
3
|
-
import "./index.css";
|
|
4
|
-
|
|
5
|
-
function App() {
|
|
6
|
-
return (
|
|
7
|
-
<div class="min-h-screen bg-slate-50" data-theme="professional">
|
|
8
|
-
<ServerStatusBar />
|
|
9
|
-
<AppRouter />
|
|
10
|
-
</div>
|
|
11
|
-
);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export default App;
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
.App {
|
|
2
|
-
text-align: center;
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
.logo {
|
|
6
|
-
animation: logo-spin infinite 20s linear;
|
|
7
|
-
height: 40vmin;
|
|
8
|
-
pointer-events: none;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
.header {
|
|
12
|
-
background-color: #282c34;
|
|
13
|
-
min-height: 100vh;
|
|
14
|
-
display: flex;
|
|
15
|
-
flex-direction: column;
|
|
16
|
-
align-items: center;
|
|
17
|
-
justify-content: center;
|
|
18
|
-
font-size: calc(10px + 2vmin);
|
|
19
|
-
color: white;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
.link {
|
|
23
|
-
color: #b318f0;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
@keyframes logo-spin {
|
|
27
|
-
from {
|
|
28
|
-
transform: rotate(0deg);
|
|
29
|
-
}
|
|
30
|
-
to {
|
|
31
|
-
transform: rotate(360deg);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
Binary file
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { For, Show } from "solid-js";
|
|
2
|
-
import { useNavigate } from "@solidjs/router";
|
|
3
|
-
import { HomeIcon, ArrowLeftIcon } from "./icons";
|
|
4
|
-
|
|
5
|
-
export default function Breadcrumbs(props) {
|
|
6
|
-
const navigate = useNavigate();
|
|
7
|
-
|
|
8
|
-
const handleNavigation = (path) => {
|
|
9
|
-
navigate(path);
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
return (
|
|
13
|
-
<div class="flex items-center gap-2 text-sm text-slate-600 mb-4">
|
|
14
|
-
<button
|
|
15
|
-
class="flex items-center gap-1 hover:text-slate-900 transition-colors"
|
|
16
|
-
onClick={() => handleNavigation("/")}
|
|
17
|
-
>
|
|
18
|
-
<HomeIcon class="w-4 h-4" />
|
|
19
|
-
<span>Home</span>
|
|
20
|
-
</button>
|
|
21
|
-
|
|
22
|
-
<For each={props.items}>
|
|
23
|
-
{(item, index) => (
|
|
24
|
-
<>
|
|
25
|
-
<ArrowLeftIcon class="w-3 h-3 rotate-180 text-slate-400" />
|
|
26
|
-
<Show when={item.path && index() < props.items.length - 1}>
|
|
27
|
-
<button
|
|
28
|
-
class="hover:text-slate-900 transition-colors"
|
|
29
|
-
onClick={() => handleNavigation(item.path)}
|
|
30
|
-
>
|
|
31
|
-
{item.label}
|
|
32
|
-
</button>
|
|
33
|
-
</Show>
|
|
34
|
-
<Show when={!item.path || index() === props.items.length - 1}>
|
|
35
|
-
<span class="text-slate-900 font-medium">{item.label}</span>
|
|
36
|
-
</Show>
|
|
37
|
-
</>
|
|
38
|
-
)}
|
|
39
|
-
</For>
|
|
40
|
-
</div>
|
|
41
|
-
);
|
|
42
|
-
}
|
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
import { Show, For, createSignal, createEffect, onMount, onCleanup } from "solid-js";
|
|
2
|
-
import { createJobLogsStream } from "../utils/api";
|
|
3
|
-
import Prism from "prismjs";
|
|
4
|
-
import "prismjs/themes/prism-tomorrow.css";
|
|
5
|
-
|
|
6
|
-
export default function LogViewer(props) {
|
|
7
|
-
const [autoScroll, setAutoScroll] = createSignal(true);
|
|
8
|
-
const [highlightedLine, setHighlightedLine] = createSignal(null);
|
|
9
|
-
|
|
10
|
-
let logContainerRef;
|
|
11
|
-
|
|
12
|
-
const {
|
|
13
|
-
logs,
|
|
14
|
-
streaming,
|
|
15
|
-
error,
|
|
16
|
-
startStreaming,
|
|
17
|
-
stopStreaming,
|
|
18
|
-
clearLogs
|
|
19
|
-
} = createJobLogsStream(props.submissionId);
|
|
20
|
-
|
|
21
|
-
// Auto-scroll functionality
|
|
22
|
-
createEffect(() => {
|
|
23
|
-
if (autoScroll() && logContainerRef && logs().length > 0) {
|
|
24
|
-
logContainerRef.scrollTop = logContainerRef.scrollHeight;
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// Check if user scrolled up manually
|
|
29
|
-
const handleScroll = () => {
|
|
30
|
-
if (!logContainerRef) return;
|
|
31
|
-
|
|
32
|
-
const { scrollTop, scrollHeight, clientHeight } = logContainerRef;
|
|
33
|
-
const isAtBottom = scrollTop + clientHeight >= scrollHeight - 10;
|
|
34
|
-
|
|
35
|
-
if (autoScroll() && !isAtBottom) {
|
|
36
|
-
setAutoScroll(false);
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const toggleAutoScroll = () => {
|
|
41
|
-
setAutoScroll(!autoScroll());
|
|
42
|
-
if (autoScroll()) {
|
|
43
|
-
scrollToBottom();
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const scrollToBottom = () => {
|
|
48
|
-
if (logContainerRef) {
|
|
49
|
-
logContainerRef.scrollTop = logContainerRef.scrollHeight;
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const copyLogs = () => {
|
|
54
|
-
const logText = logs()
|
|
55
|
-
.map(log => {
|
|
56
|
-
const timestamp = log.timestamp ? `[${formatTimestamp(log.timestamp)}] ` : '';
|
|
57
|
-
const level = log.level ? `[${log.level.toUpperCase()}] ` : '';
|
|
58
|
-
return `${timestamp}${level}${log.message}`;
|
|
59
|
-
})
|
|
60
|
-
.join('\n');
|
|
61
|
-
|
|
62
|
-
navigator.clipboard.writeText(logText).then(() => {
|
|
63
|
-
// Could add a toast notification here
|
|
64
|
-
});
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const formatTimestamp = (timestamp) => {
|
|
68
|
-
try {
|
|
69
|
-
const date = new Date(timestamp);
|
|
70
|
-
return date.toLocaleTimeString("en-US", {
|
|
71
|
-
hour12: false,
|
|
72
|
-
hour: "2-digit",
|
|
73
|
-
minute: "2-digit",
|
|
74
|
-
second: "2-digit",
|
|
75
|
-
});
|
|
76
|
-
} catch {
|
|
77
|
-
return timestamp;
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const formatLogMessage = (message) => {
|
|
82
|
-
// Escape HTML first
|
|
83
|
-
let formatted = message
|
|
84
|
-
.replace(/&/g, "&")
|
|
85
|
-
.replace(/</g, "<")
|
|
86
|
-
.replace(/>/g, ">");
|
|
87
|
-
|
|
88
|
-
// Handle ANSI escape codes and convert to HTML
|
|
89
|
-
formatted = formatted
|
|
90
|
-
// Reset
|
|
91
|
-
.replace(/\x1b\[0m/g, "</span>")
|
|
92
|
-
// Colors
|
|
93
|
-
.replace(/\x1b\[31m/g, '<span class="ansi-red">')
|
|
94
|
-
.replace(/\x1b\[32m/g, '<span class="ansi-green">')
|
|
95
|
-
.replace(/\x1b\[33m/g, '<span class="ansi-yellow">')
|
|
96
|
-
.replace(/\x1b\[34m/g, '<span class="ansi-blue">')
|
|
97
|
-
.replace(/\x1b\[35m/g, '<span class="ansi-magenta">')
|
|
98
|
-
.replace(/\x1b\[36m/g, '<span class="ansi-cyan">')
|
|
99
|
-
.replace(/\x1b\[37m/g, '<span class="ansi-white">')
|
|
100
|
-
.replace(/\x1b\[90m/g, '<span class="ansi-gray">')
|
|
101
|
-
.replace(/\x1b\[91m/g, '<span class="ansi-bright-red">')
|
|
102
|
-
.replace(/\x1b\[92m/g, '<span class="ansi-bright-green">')
|
|
103
|
-
.replace(/\x1b\[93m/g, '<span class="ansi-bright-yellow">')
|
|
104
|
-
.replace(/\x1b\[94m/g, '<span class="ansi-bright-blue">')
|
|
105
|
-
.replace(/\x1b\[95m/g, '<span class="ansi-bright-magenta">')
|
|
106
|
-
.replace(/\x1b\[96m/g, '<span class="ansi-bright-cyan">')
|
|
107
|
-
.replace(/\x1b\[97m/g, '<span class="ansi-bright-white">')
|
|
108
|
-
// Bold
|
|
109
|
-
.replace(/\x1b\[1m/g, '<span class="ansi-bold">')
|
|
110
|
-
// Remove any remaining ANSI codes
|
|
111
|
-
.replace(/\x1b\[[0-9;]*m/g, "");
|
|
112
|
-
|
|
113
|
-
// Try to detect and highlight JSON
|
|
114
|
-
if (message.trim().match(/^\s*[{\[].*[}\]]\s*$/s)) {
|
|
115
|
-
try {
|
|
116
|
-
const trimmed = message.trim();
|
|
117
|
-
const parsed = JSON.parse(trimmed);
|
|
118
|
-
const jsonString = JSON.stringify(parsed, null, 2);
|
|
119
|
-
formatted = Prism.highlight(jsonString, Prism.languages.json, "json");
|
|
120
|
-
} catch {
|
|
121
|
-
// Not valid JSON, continue with original formatting
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Highlight URLs
|
|
126
|
-
formatted = formatted.replace(
|
|
127
|
-
/(https?:\/\/[^\s<>&"']+)/g,
|
|
128
|
-
'<a href="$1" target="_blank" rel="noopener noreferrer" class="log-url">$1</a>'
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
// Highlight file paths
|
|
132
|
-
formatted = formatted.replace(
|
|
133
|
-
/([a-zA-Z0-9_\-./]+\.(py|js|ts|vue|json|yaml|yml|txt|log|sh|md))/g,
|
|
134
|
-
'<span class="log-filepath">$1</span>'
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
// Highlight common patterns
|
|
138
|
-
formatted = formatted
|
|
139
|
-
// Error patterns
|
|
140
|
-
.replace(
|
|
141
|
-
/\b(ERROR|FATAL|EXCEPTION|TRACEBACK)\b/gi,
|
|
142
|
-
'<span class="pattern-error">$1</span>'
|
|
143
|
-
)
|
|
144
|
-
// Warning patterns
|
|
145
|
-
.replace(
|
|
146
|
-
/\b(WARNING|WARN|DEPRECATED)\b/gi,
|
|
147
|
-
'<span class="pattern-warning">$1</span>'
|
|
148
|
-
)
|
|
149
|
-
// Success patterns
|
|
150
|
-
.replace(
|
|
151
|
-
/\b(SUCCESS|PASSED|OK|COMPLETE)\b/gi,
|
|
152
|
-
'<span class="pattern-success">$1</span>'
|
|
153
|
-
)
|
|
154
|
-
// Info patterns
|
|
155
|
-
.replace(
|
|
156
|
-
/\b(INFO|DEBUG|TRACE)\b/gi,
|
|
157
|
-
'<span class="pattern-info">$1</span>'
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
return formatted;
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const getLogLevelClass = (level) => {
|
|
164
|
-
if (!level) return "";
|
|
165
|
-
|
|
166
|
-
switch (level.toLowerCase()) {
|
|
167
|
-
case "error":
|
|
168
|
-
return "log-level-error";
|
|
169
|
-
case "warn":
|
|
170
|
-
case "warning":
|
|
171
|
-
return "log-level-warn";
|
|
172
|
-
case "info":
|
|
173
|
-
return "log-level-info";
|
|
174
|
-
case "debug":
|
|
175
|
-
return "log-level-debug";
|
|
176
|
-
default:
|
|
177
|
-
return "";
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
const highlightLine = (index) => {
|
|
182
|
-
setHighlightedLine(highlightedLine() === index ? null : index);
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
onMount(() => {
|
|
186
|
-
startStreaming();
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
onCleanup(() => {
|
|
190
|
-
stopStreaming();
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
return (
|
|
194
|
-
<div class="github-log-viewer">
|
|
195
|
-
{/* Header */}
|
|
196
|
-
<div class="log-header">
|
|
197
|
-
<div class="log-title">
|
|
198
|
-
<svg class="octicon" viewBox="0 0 16 16" width="16" height="16">
|
|
199
|
-
<path d="M8 16a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2c.55 0 1.02.26 1.3.62.27.35.7.38 1.1.38.4 0 .83-.03 1.1-.38.28-.36.75-.62 1.3-.62a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H8z" />
|
|
200
|
-
</svg>
|
|
201
|
-
<span>Logs</span>
|
|
202
|
-
</div>
|
|
203
|
-
<div class="log-controls">
|
|
204
|
-
<button
|
|
205
|
-
onClick={toggleAutoScroll}
|
|
206
|
-
class={`control-btn ${autoScroll() ? 'active' : ''}`}
|
|
207
|
-
>
|
|
208
|
-
Auto-scroll
|
|
209
|
-
</button>
|
|
210
|
-
<button onClick={clearLogs} class="control-btn">Clear</button>
|
|
211
|
-
<button onClick={copyLogs} class="control-btn">Copy</button>
|
|
212
|
-
</div>
|
|
213
|
-
</div>
|
|
214
|
-
|
|
215
|
-
{/* Error State */}
|
|
216
|
-
<Show when={error()}>
|
|
217
|
-
<div class="alert alert-error mb-2">
|
|
218
|
-
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
|
|
219
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
220
|
-
</svg>
|
|
221
|
-
<span>{error()}</span>
|
|
222
|
-
</div>
|
|
223
|
-
</Show>
|
|
224
|
-
|
|
225
|
-
{/* Container */}
|
|
226
|
-
<div class="log-container" ref={logContainerRef} onScroll={handleScroll}>
|
|
227
|
-
<Show when={logs().length === 0 && !error()}>
|
|
228
|
-
<div class="log-empty">
|
|
229
|
-
<div class="empty-state">
|
|
230
|
-
<svg class="empty-icon" viewBox="0 0 24 24" width="24" height="24">
|
|
231
|
-
<path d="M3 3v18h18V3H3zm16 16H5V5h14v14zM7 7h10v2H7V7zm0 4h10v2H7v-2zm0 4h7v2H7v-2z" />
|
|
232
|
-
</svg>
|
|
233
|
-
<p>No logs available</p>
|
|
234
|
-
<p class="empty-subtitle">Logs will appear here when available</p>
|
|
235
|
-
</div>
|
|
236
|
-
</div>
|
|
237
|
-
</Show>
|
|
238
|
-
|
|
239
|
-
<Show when={logs().length > 0}>
|
|
240
|
-
<div class="log-content">
|
|
241
|
-
<For each={logs()}>
|
|
242
|
-
{(log, index) => (
|
|
243
|
-
<div
|
|
244
|
-
class={`log-line ${getLogLevelClass(log.level)} ${
|
|
245
|
-
highlightedLine() === index() ? 'highlighted' : ''
|
|
246
|
-
}`}
|
|
247
|
-
onClick={() => highlightLine(index())}
|
|
248
|
-
>
|
|
249
|
-
<span class="line-number">{index() + 1}</span>
|
|
250
|
-
<Show when={log.timestamp}>
|
|
251
|
-
<span class="log-timestamp">
|
|
252
|
-
{formatTimestamp(log.timestamp)}
|
|
253
|
-
</span>
|
|
254
|
-
</Show>
|
|
255
|
-
<Show when={log.level}>
|
|
256
|
-
<span class="log-level-badge">
|
|
257
|
-
{log.level.toUpperCase().charAt(0)}
|
|
258
|
-
</span>
|
|
259
|
-
</Show>
|
|
260
|
-
<span class="log-message" innerHTML={formatLogMessage(log.message)} />
|
|
261
|
-
</div>
|
|
262
|
-
)}
|
|
263
|
-
</For>
|
|
264
|
-
</div>
|
|
265
|
-
</Show>
|
|
266
|
-
</div>
|
|
267
|
-
|
|
268
|
-
{/* Footer */}
|
|
269
|
-
<div class="log-footer">
|
|
270
|
-
<span class="log-count">{logs().length} lines</span>
|
|
271
|
-
<Show when={streaming()}>
|
|
272
|
-
<span class="streaming-status">
|
|
273
|
-
<div class="streaming-dot"></div>
|
|
274
|
-
Streaming...
|
|
275
|
-
</span>
|
|
276
|
-
</Show>
|
|
277
|
-
</div>
|
|
278
|
-
</div>
|
|
279
|
-
);
|
|
280
|
-
}
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import { Show, createSignal } from "solid-js";
|
|
2
|
-
import Prism from "prismjs";
|
|
3
|
-
import "prismjs/themes/prism.css";
|
|
4
|
-
import "prismjs/components/prism-json";
|
|
5
|
-
import "prismjs/components/prism-bash";
|
|
6
|
-
import "prismjs/components/prism-python";
|
|
7
|
-
|
|
8
|
-
export default function MessageViewer(props) {
|
|
9
|
-
const [isExpanded, setIsExpanded] = createSignal(false);
|
|
10
|
-
|
|
11
|
-
const message = () => props.message || '';
|
|
12
|
-
const isLongMessage = () => message().length > 500;
|
|
13
|
-
|
|
14
|
-
const displayMessage = () => {
|
|
15
|
-
if (isExpanded() || !isLongMessage()) {
|
|
16
|
-
return message();
|
|
17
|
-
}
|
|
18
|
-
return message().substring(0, 500) + '...';
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const copyMessage = () => {
|
|
22
|
-
navigator.clipboard.writeText(message()).then(() => {
|
|
23
|
-
// Could add a toast notification here
|
|
24
|
-
});
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const formatMessage = (msg) => {
|
|
28
|
-
if (!msg) return "";
|
|
29
|
-
|
|
30
|
-
// Escape HTML first
|
|
31
|
-
let formatted = msg
|
|
32
|
-
.replace(/&/g, "&")
|
|
33
|
-
.replace(/</g, "<")
|
|
34
|
-
.replace(/>/g, ">");
|
|
35
|
-
|
|
36
|
-
// Handle ANSI escape codes and convert to HTML
|
|
37
|
-
formatted = formatted
|
|
38
|
-
// Reset
|
|
39
|
-
.replace(/\x1b\[0m/g, "</span>")
|
|
40
|
-
// Colors
|
|
41
|
-
.replace(/\x1b\[31m/g, '<span class="ansi-red">')
|
|
42
|
-
.replace(/\x1b\[32m/g, '<span class="ansi-green">')
|
|
43
|
-
.replace(/\x1b\[33m/g, '<span class="ansi-yellow">')
|
|
44
|
-
.replace(/\x1b\[34m/g, '<span class="ansi-blue">')
|
|
45
|
-
.replace(/\x1b\[35m/g, '<span class="ansi-magenta">')
|
|
46
|
-
.replace(/\x1b\[36m/g, '<span class="ansi-cyan">')
|
|
47
|
-
.replace(/\x1b\[37m/g, '<span class="ansi-white">')
|
|
48
|
-
.replace(/\x1b\[90m/g, '<span class="ansi-gray">')
|
|
49
|
-
.replace(/\x1b\[91m/g, '<span class="ansi-bright-red">')
|
|
50
|
-
.replace(/\x1b\[92m/g, '<span class="ansi-bright-green">')
|
|
51
|
-
.replace(/\x1b\[93m/g, '<span class="ansi-bright-yellow">')
|
|
52
|
-
.replace(/\x1b\[94m/g, '<span class="ansi-bright-blue">')
|
|
53
|
-
.replace(/\x1b\[95m/g, '<span class="ansi-bright-magenta">')
|
|
54
|
-
.replace(/\x1b\[96m/g, '<span class="ansi-bright-cyan">')
|
|
55
|
-
.replace(/\x1b\[97m/g, '<span class="ansi-bright-white">')
|
|
56
|
-
// Bold
|
|
57
|
-
.replace(/\x1b\[1m/g, '<span class="ansi-bold">')
|
|
58
|
-
// Remove any remaining ANSI codes
|
|
59
|
-
.replace(/\x1b\[[0-9;]*m/g, "");
|
|
60
|
-
|
|
61
|
-
// Try to detect and highlight JSON
|
|
62
|
-
if (msg.trim().match(/^\s*[{\[].*[}\]]\s*$/s)) {
|
|
63
|
-
try {
|
|
64
|
-
const trimmed = msg.trim();
|
|
65
|
-
const parsed = JSON.parse(trimmed);
|
|
66
|
-
const jsonString = JSON.stringify(parsed, null, 2);
|
|
67
|
-
formatted = Prism.highlight(jsonString, Prism.languages.json, "json");
|
|
68
|
-
} catch {
|
|
69
|
-
// Not valid JSON, continue with original formatting
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Highlight URLs
|
|
74
|
-
formatted = formatted.replace(
|
|
75
|
-
/(https?:\/\/[^\s<>&"']+)/g,
|
|
76
|
-
'<a href="$1" target="_blank" rel="noopener noreferrer" class="message-url">$1</a>'
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
// Highlight file paths
|
|
80
|
-
formatted = formatted.replace(
|
|
81
|
-
/([a-zA-Z0-9_\-./]+\.(py|js|ts|vue|json|yaml|yml|txt|log|sh|md))/g,
|
|
82
|
-
'<span class="message-filepath">$1</span>'
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
// Highlight common patterns
|
|
86
|
-
formatted = formatted
|
|
87
|
-
// Error patterns
|
|
88
|
-
.replace(
|
|
89
|
-
/\b(ERROR|FATAL|EXCEPTION|TRACEBACK)\b/gi,
|
|
90
|
-
'<span class="pattern-error">$1</span>'
|
|
91
|
-
)
|
|
92
|
-
// Warning patterns
|
|
93
|
-
.replace(
|
|
94
|
-
/\b(WARNING|WARN|DEPRECATED)\b/gi,
|
|
95
|
-
'<span class="pattern-warning">$1</span>'
|
|
96
|
-
)
|
|
97
|
-
// Success patterns
|
|
98
|
-
.replace(
|
|
99
|
-
/\b(SUCCESS|PASSED|OK|COMPLETE)\b/gi,
|
|
100
|
-
'<span class="pattern-success">$1</span>'
|
|
101
|
-
)
|
|
102
|
-
// Info patterns
|
|
103
|
-
.replace(
|
|
104
|
-
/\b(INFO|DEBUG|TRACE)\b/gi,
|
|
105
|
-
'<span class="pattern-info">$1</span>'
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
return formatted;
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
// Check if message looks like JSON
|
|
112
|
-
const isJsonMessage = () => {
|
|
113
|
-
const trimmed = message().trim();
|
|
114
|
-
return (trimmed.startsWith('{') && trimmed.endsWith('}')) ||
|
|
115
|
-
(trimmed.startsWith('[') && trimmed.endsWith(']'));
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
// Check if message looks like a stack trace
|
|
119
|
-
const isStackTrace = () => {
|
|
120
|
-
return message().includes('Traceback') ||
|
|
121
|
-
message().includes(' at ') ||
|
|
122
|
-
message().includes('File "') ||
|
|
123
|
-
/^\s*File ".*", line \d+/.test(message());
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const getMessageClass = () => {
|
|
127
|
-
if (isStackTrace()) {
|
|
128
|
-
return 'bg-red-50 border-red-200';
|
|
129
|
-
} else if (isJsonMessage()) {
|
|
130
|
-
return 'bg-slate-50 border-slate-200';
|
|
131
|
-
} else {
|
|
132
|
-
return 'bg-slate-50 border-slate-200';
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
return (
|
|
137
|
-
<div class={`message-viewer rounded-lg border overflow-hidden ${getMessageClass()}`}>
|
|
138
|
-
<div class="relative">
|
|
139
|
-
<div
|
|
140
|
-
class="message-content p-4 text-slate-900 whitespace-pre-wrap break-words max-h-96 overflow-y-auto scrollbar-thin scrollbar-thumb-slate-300 scrollbar-track-slate-100 font-mono text-sm leading-relaxed"
|
|
141
|
-
innerHTML={formatMessage(displayMessage())}
|
|
142
|
-
/>
|
|
143
|
-
|
|
144
|
-
<Show when={isLongMessage() && !isExpanded()}>
|
|
145
|
-
<div class="absolute bottom-0 left-0 right-0 h-8 bg-gradient-to-t from-white to-transparent"></div>
|
|
146
|
-
</Show>
|
|
147
|
-
</div>
|
|
148
|
-
</div>
|
|
149
|
-
);
|
|
150
|
-
}
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import { useLocation, useNavigate } from "@solidjs/router";
|
|
2
|
-
import { createSignal, Show } from "solid-js";
|
|
3
|
-
import { MenuIcon, CloseIcon, DocumentIcon } from "./icons";
|
|
4
|
-
import { createStatusResource } from "../utils/api";
|
|
5
|
-
|
|
6
|
-
export default function NavigationSidebar() {
|
|
7
|
-
const location = useLocation();
|
|
8
|
-
const navigate = useNavigate();
|
|
9
|
-
const [isMobileMenuOpen, setIsMobileMenuOpen] = createSignal(false);
|
|
10
|
-
|
|
11
|
-
// Get status for version info
|
|
12
|
-
const { data: status } = createStatusResource();
|
|
13
|
-
|
|
14
|
-
const navigationItems = [
|
|
15
|
-
{
|
|
16
|
-
id: "home",
|
|
17
|
-
label: "Jobs",
|
|
18
|
-
path: "/",
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
id: "applications",
|
|
22
|
-
label: "Serve Application",
|
|
23
|
-
path: "/serve_applications",
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
id: "plugin",
|
|
27
|
-
label: "Plugin",
|
|
28
|
-
path: "/plugin",
|
|
29
|
-
}
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
const isActive = (path) => {
|
|
33
|
-
if (path === "/") {
|
|
34
|
-
return location.pathname === "/";
|
|
35
|
-
}
|
|
36
|
-
return location.pathname.startsWith(path);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const handleNavigation = (path) => {
|
|
40
|
-
navigate(path);
|
|
41
|
-
setIsMobileMenuOpen(false); // Close mobile menu after navigation
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
return (
|
|
45
|
-
<>
|
|
46
|
-
{/* Mobile Menu Button */}
|
|
47
|
-
<button
|
|
48
|
-
class="lg:hidden fixed top-20 left-4 z-50 btn btn-sm btn-circle btn-ghost bg-white border border-slate-200 shadow-sm"
|
|
49
|
-
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen())}
|
|
50
|
-
>
|
|
51
|
-
<Show when={!isMobileMenuOpen()}>
|
|
52
|
-
<MenuIcon class="w-4 h-4" />
|
|
53
|
-
</Show>
|
|
54
|
-
<Show when={isMobileMenuOpen()}>
|
|
55
|
-
<CloseIcon class="w-4 h-4" />
|
|
56
|
-
</Show>
|
|
57
|
-
</button>
|
|
58
|
-
|
|
59
|
-
{/* Mobile Overlay */}
|
|
60
|
-
<Show when={isMobileMenuOpen()}>
|
|
61
|
-
<div
|
|
62
|
-
class="lg:hidden fixed inset-0 bg-black bg-opacity-30 z-40"
|
|
63
|
-
onClick={() => setIsMobileMenuOpen(false)}
|
|
64
|
-
/>
|
|
65
|
-
</Show>
|
|
66
|
-
|
|
67
|
-
{/* Sidebar */}
|
|
68
|
-
<div class={`
|
|
69
|
-
w-64 bg-white border-r border-slate-200 min-h-screen flex flex-col
|
|
70
|
-
lg:relative lg:translate-x-0 lg:z-auto
|
|
71
|
-
${isMobileMenuOpen()
|
|
72
|
-
? 'fixed inset-y-0 left-0 z-50 translate-x-0'
|
|
73
|
-
: 'fixed inset-y-0 left-0 z-50 -translate-x-full lg:translate-x-0'}
|
|
74
|
-
transition-transform duration-300 ease-in-out
|
|
75
|
-
`}>
|
|
76
|
-
{/* Header */}
|
|
77
|
-
<div class="p-6 border-b border-slate-200">
|
|
78
|
-
<h2 class="text-xl font-bold text-slate-900">Synapse Devtools</h2>
|
|
79
|
-
<p class="text-sm text-slate-600 mt-1">Development Environment</p>
|
|
80
|
-
</div>
|
|
81
|
-
|
|
82
|
-
{/* Navigation */}
|
|
83
|
-
<nav class="flex-1 p-6 py-8 overflow-y-auto">
|
|
84
|
-
<ul class="space-y-2">
|
|
85
|
-
{navigationItems.map((item) => {
|
|
86
|
-
const active = isActive(item.path);
|
|
87
|
-
|
|
88
|
-
return (
|
|
89
|
-
<li key={item.id}>
|
|
90
|
-
<button
|
|
91
|
-
class={`w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-left transition-colors ${
|
|
92
|
-
active
|
|
93
|
-
? "bg-slate-100 text-slate-900 font-medium"
|
|
94
|
-
: "text-slate-600 hover:bg-slate-50 hover:text-slate-900"
|
|
95
|
-
}`}
|
|
96
|
-
onClick={() => handleNavigation(item.path)}
|
|
97
|
-
>
|
|
98
|
-
<div class="flex-1 min-w-0">
|
|
99
|
-
<div class="text-sm font-medium">{item.label}</div>
|
|
100
|
-
</div>
|
|
101
|
-
</button>
|
|
102
|
-
</li>
|
|
103
|
-
);
|
|
104
|
-
})}
|
|
105
|
-
</ul>
|
|
106
|
-
</nav>
|
|
107
|
-
|
|
108
|
-
{/* Footer - Fixed at bottom */}
|
|
109
|
-
<div class="flex-shrink-0 p-6 border-t border-slate-200 bg-white">
|
|
110
|
-
<div class="text-xs text-slate-500">
|
|
111
|
-
<div class="flex items-center justify-between">
|
|
112
|
-
<span>SDK {status()?.devtools?.version || "Unknown"}</span>
|
|
113
|
-
<a
|
|
114
|
-
href="https://docs.synapse.sh"
|
|
115
|
-
target="_blank"
|
|
116
|
-
rel="noopener noreferrer"
|
|
117
|
-
class="text-slate-600 hover:text-slate-900"
|
|
118
|
-
title="Documentation"
|
|
119
|
-
>
|
|
120
|
-
<DocumentIcon class="w-4 h-4" />
|
|
121
|
-
</a>
|
|
122
|
-
</div>
|
|
123
|
-
</div>
|
|
124
|
-
</div>
|
|
125
|
-
</div>
|
|
126
|
-
</>
|
|
127
|
-
);
|
|
128
|
-
}
|