mcpgraph-ux 0.1.2 → 0.1.4

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,124 @@
1
+ .container {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 12px;
5
+ padding: 12px;
6
+ background: #f5f5f5;
7
+ border: none;
8
+ border-radius: 0;
9
+ margin-bottom: 0;
10
+ }
11
+
12
+ .controlsRow {
13
+ display: flex;
14
+ align-items: center;
15
+ justify-content: space-between;
16
+ gap: 16px;
17
+ }
18
+
19
+ .statusInfo {
20
+ display: flex;
21
+ align-items: center;
22
+ gap: 8px;
23
+ font-size: 13px;
24
+ margin-left: auto;
25
+ }
26
+
27
+ .statusLabel {
28
+ font-weight: 600;
29
+ color: #666;
30
+ }
31
+
32
+ .statusBadge {
33
+ padding: 4px 8px;
34
+ border-radius: 4px;
35
+ font-size: 11px;
36
+ font-weight: 600;
37
+ text-transform: uppercase;
38
+ letter-spacing: 0.5px;
39
+ }
40
+
41
+ .statusBadge.not_started {
42
+ background: #e0e0e0;
43
+ color: #666;
44
+ }
45
+
46
+ .statusBadge.running {
47
+ background: #e3f2fd;
48
+ color: #1976d2;
49
+ animation: pulse 2s ease-in-out infinite;
50
+ }
51
+
52
+ .statusBadge.paused {
53
+ background: #fff3e0;
54
+ color: #f57c00;
55
+ }
56
+
57
+ .statusBadge.finished {
58
+ background: #e8f5e9;
59
+ color: #2e7d32;
60
+ }
61
+
62
+ .statusBadge.error {
63
+ background: #ffebee;
64
+ color: #c62828;
65
+ }
66
+
67
+ @keyframes pulse {
68
+ 0%, 100% {
69
+ opacity: 1;
70
+ }
71
+ 50% {
72
+ opacity: 0.7;
73
+ }
74
+ }
75
+
76
+ .separator {
77
+ color: #999;
78
+ }
79
+
80
+ .currentNode {
81
+ color: #666;
82
+ font-size: 12px;
83
+ }
84
+
85
+ .currentNode code {
86
+ background: #fff;
87
+ padding: 2px 6px;
88
+ border-radius: 3px;
89
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
90
+ font-size: 11px;
91
+ color: #333;
92
+ border: 1px solid #ddd;
93
+ }
94
+
95
+ .controls {
96
+ display: flex;
97
+ gap: 8px;
98
+ }
99
+
100
+ .button {
101
+ padding: 8px 16px;
102
+ background: #2196f3;
103
+ color: white;
104
+ border: none;
105
+ border-radius: 4px;
106
+ font-size: 13px;
107
+ font-weight: 500;
108
+ cursor: pointer;
109
+ transition: background-color 0.2s;
110
+ display: flex;
111
+ align-items: center;
112
+ gap: 6px;
113
+ }
114
+
115
+ .button:hover:not(:disabled) {
116
+ background: #1976d2;
117
+ }
118
+
119
+ .button:disabled {
120
+ background: #ccc;
121
+ cursor: not-allowed;
122
+ opacity: 0.6;
123
+ }
124
+
@@ -0,0 +1,209 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect } from 'react';
4
+ import styles from './DebugControls.module.css';
5
+
6
+ export type ExecutionStatus = 'not_started' | 'running' | 'paused' | 'finished' | 'error' | 'stopped';
7
+
8
+ interface DebugControlsProps {
9
+ executionId: string | null;
10
+ status: ExecutionStatus;
11
+ currentNodeId: string | null;
12
+ onStatusChange?: (status: ExecutionStatus) => void;
13
+ onRun?: () => void;
14
+ onStepFromStart?: () => void;
15
+ onClear?: () => void;
16
+ disabled?: boolean;
17
+ }
18
+
19
+ export default function DebugControls({
20
+ executionId,
21
+ status,
22
+ currentNodeId,
23
+ onStatusChange,
24
+ onRun,
25
+ onStepFromStart,
26
+ onClear,
27
+ disabled,
28
+ }: DebugControlsProps) {
29
+ const [isProcessing, setIsProcessing] = useState(false);
30
+
31
+ const handlePause = async () => {
32
+ if (!executionId || status !== 'running') return;
33
+
34
+ setIsProcessing(true);
35
+ try {
36
+ const response = await fetch('/api/execution/controller', {
37
+ method: 'POST',
38
+ headers: { 'Content-Type': 'application/json' },
39
+ body: JSON.stringify({ executionId, action: 'pause' }),
40
+ });
41
+
42
+ const data = await response.json();
43
+ if (data.error) {
44
+ console.error('Error pausing:', data.error);
45
+ }
46
+ // Don't set status here - onPause hook will send stateUpdate with actual state
47
+ } catch (error) {
48
+ console.error('Error pausing execution:', error);
49
+ } finally {
50
+ setIsProcessing(false);
51
+ }
52
+ };
53
+
54
+ const handleResume = async () => {
55
+ if (!executionId || status !== 'paused') return;
56
+
57
+ setIsProcessing(true);
58
+ try {
59
+ const response = await fetch('/api/execution/controller', {
60
+ method: 'POST',
61
+ headers: { 'Content-Type': 'application/json' },
62
+ body: JSON.stringify({ executionId, action: 'resume' }),
63
+ });
64
+
65
+ const data = await response.json();
66
+ if (data.error) {
67
+ console.error('Error resuming:', data.error);
68
+ }
69
+ // Don't set status here - hooks will tell us the actual state
70
+ } catch (error) {
71
+ console.error('Error resuming execution:', error);
72
+ } finally {
73
+ setIsProcessing(false);
74
+ }
75
+ };
76
+
77
+ const handleStep = async () => {
78
+ if (!executionId || status !== 'paused') return;
79
+
80
+ setIsProcessing(true);
81
+ try {
82
+ const response = await fetch('/api/execution/controller', {
83
+ method: 'POST',
84
+ headers: { 'Content-Type': 'application/json' },
85
+ body: JSON.stringify({ executionId, action: 'step' }),
86
+ });
87
+
88
+ const data = await response.json();
89
+ if (data.error) {
90
+ console.error('Error stepping:', data.error);
91
+ }
92
+ // Don't set status here - onPause hook will send stateUpdate with actual state after step
93
+ } catch (error) {
94
+ console.error('Error stepping execution:', error);
95
+ } finally {
96
+ setIsProcessing(false);
97
+ }
98
+ };
99
+
100
+ const handleRun = () => {
101
+ if (onRun) {
102
+ onRun();
103
+ }
104
+ };
105
+
106
+ const handleStop = async () => {
107
+ if (!executionId || (status !== 'running' && status !== 'paused')) return;
108
+
109
+ setIsProcessing(true);
110
+ try {
111
+ const response = await fetch('/api/execution/controller', {
112
+ method: 'POST',
113
+ headers: { 'Content-Type': 'application/json' },
114
+ body: JSON.stringify({ executionId, action: 'stop' }),
115
+ });
116
+
117
+ const data = await response.json();
118
+ if (data.error) {
119
+ console.error('Error stopping:', data.error);
120
+ }
121
+ // Don't set status here - executionStopped event will be sent and handled in ToolTester
122
+ } catch (error) {
123
+ console.error('Error stopping execution:', error);
124
+ } finally {
125
+ setIsProcessing(false);
126
+ }
127
+ };
128
+
129
+ const handleClear = () => {
130
+ if (onClear) {
131
+ onClear();
132
+ }
133
+ };
134
+
135
+ return (
136
+ <div className={styles.container}>
137
+ <div className={styles.controlsRow}>
138
+ <div className={styles.controls}>
139
+ <button
140
+ onClick={handleRun}
141
+ disabled={disabled || isProcessing || status === 'running' || status === 'paused'}
142
+ className={styles.button}
143
+ title="Run execution"
144
+ >
145
+ ▶ Run
146
+ </button>
147
+ <button
148
+ onClick={status === 'paused' ? handleStep : (onStepFromStart || handleRun)}
149
+ disabled={isProcessing || (status !== 'paused' && status !== 'not_started' && status !== 'finished' && status !== 'error' && status !== 'stopped') || (status === 'not_started' && disabled)}
150
+ className={styles.button}
151
+ title={status === 'paused' ? 'Step to next node' : 'Start execution and pause at first node'}
152
+ >
153
+ ⏭ Step
154
+ </button>
155
+ <button
156
+ onClick={handlePause}
157
+ disabled={isProcessing || status !== 'running'}
158
+ className={styles.button}
159
+ title="Pause execution"
160
+ >
161
+ ⏸ Pause
162
+ </button>
163
+ <button
164
+ onClick={handleResume}
165
+ disabled={isProcessing || status !== 'paused'}
166
+ className={styles.button}
167
+ title="Resume execution"
168
+ >
169
+ ▶ Resume
170
+ </button>
171
+ <button
172
+ onClick={handleStop}
173
+ disabled={isProcessing || (status !== 'running' && status !== 'paused')}
174
+ className={styles.button}
175
+ title="Stop/cancel execution"
176
+ >
177
+ ⏹ Stop
178
+ </button>
179
+ <button
180
+ onClick={handleClear}
181
+ disabled={isProcessing || (status !== 'finished' && status !== 'error' && status !== 'stopped')}
182
+ className={styles.button}
183
+ title="Clear execution history and reset state"
184
+ >
185
+ × Clear
186
+ </button>
187
+ </div>
188
+
189
+ {(status === 'running' || status === 'paused' || status === 'finished' || status === 'error' || status === 'stopped') && (
190
+ <div className={styles.statusInfo}>
191
+ {currentNodeId && (
192
+ <>
193
+ <span className={styles.currentNode}>
194
+ Current: <code>{currentNodeId}</code>
195
+ </span>
196
+ <span className={styles.separator}>•</span>
197
+ </>
198
+ )}
199
+ <span className={styles.statusLabel}>Status:</span>
200
+ <span className={`${styles.statusBadge} ${styles[status]}`}>
201
+ {status.toUpperCase()}
202
+ </span>
203
+ </div>
204
+ )}
205
+ </div>
206
+ </div>
207
+ );
208
+ }
209
+
@@ -0,0 +1,371 @@
1
+ .container {
2
+ width: 100%;
3
+ height: 100%;
4
+ display: flex;
5
+ flex-direction: column;
6
+ background: white;
7
+ overflow: hidden;
8
+ }
9
+
10
+
11
+ .empty {
12
+ padding: 24px;
13
+ text-align: center;
14
+ color: #999;
15
+ font-style: italic;
16
+ }
17
+
18
+ .list {
19
+ overflow-y: auto;
20
+ flex: 1;
21
+ min-height: 0;
22
+ }
23
+
24
+ .item {
25
+ border-bottom: 1px solid #e0e0e0;
26
+ transition: background-color 0.2s;
27
+ }
28
+
29
+ .item:hover {
30
+ background-color: #f9f9f9;
31
+ }
32
+
33
+ .item.error {
34
+ border-left: 4px solid #ef5350;
35
+ }
36
+
37
+ .header {
38
+ display: flex;
39
+ align-items: center;
40
+ justify-content: space-between;
41
+ padding: 12px 16px;
42
+ cursor: pointer;
43
+ user-select: none;
44
+ }
45
+
46
+ .nodeInfo {
47
+ display: flex;
48
+ align-items: center;
49
+ gap: 8px;
50
+ flex: 1;
51
+ min-width: 0;
52
+ }
53
+
54
+ .nodeId {
55
+ font-weight: 600;
56
+ color: #333;
57
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
58
+ font-size: 13px;
59
+ }
60
+
61
+ .nodeType {
62
+ font-size: 11px;
63
+ color: #666;
64
+ background: #e8e8e8;
65
+ padding: 2px 6px;
66
+ border-radius: 4px;
67
+ text-transform: uppercase;
68
+ }
69
+
70
+ .errorBadge {
71
+ font-size: 10px;
72
+ color: white;
73
+ background: #ef5350;
74
+ padding: 2px 6px;
75
+ border-radius: 4px;
76
+ font-weight: 600;
77
+ }
78
+
79
+ .timing {
80
+ display: flex;
81
+ align-items: center;
82
+ gap: 12px;
83
+ margin-left: 12px;
84
+ }
85
+
86
+ .duration {
87
+ font-weight: 600;
88
+ color: #2196f3;
89
+ font-size: 12px;
90
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
91
+ }
92
+
93
+ .time {
94
+ font-size: 11px;
95
+ color: #999;
96
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
97
+ }
98
+
99
+ .expandButton {
100
+ background: none;
101
+ border: none;
102
+ cursor: pointer;
103
+ font-size: 10px;
104
+ color: #666;
105
+ padding: 4px 8px;
106
+ margin-left: 8px;
107
+ }
108
+
109
+ .expandButton:hover {
110
+ color: #333;
111
+ }
112
+
113
+ .details {
114
+ padding: 12px 16px;
115
+ background: #fafafa;
116
+ border-top: 1px solid #e0e0e0;
117
+ }
118
+
119
+ .errorSection {
120
+ margin-bottom: 16px;
121
+ padding: 12px;
122
+ background: #ffebee;
123
+ border-radius: 4px;
124
+ border-left: 4px solid #ef5350;
125
+ }
126
+
127
+ .errorSection strong {
128
+ display: block;
129
+ margin-bottom: 8px;
130
+ color: #c62828;
131
+ }
132
+
133
+ .errorMessage {
134
+ margin: 0;
135
+ font-size: 12px;
136
+ color: #c62828;
137
+ white-space: pre-wrap;
138
+ word-break: break-word;
139
+ }
140
+
141
+
142
+ .errorTypeBadge {
143
+ display: inline-block;
144
+ margin-bottom: 8px;
145
+ padding: 4px 8px;
146
+ background: rgba(198, 40, 40, 0.1);
147
+ border: 1px solid rgba(198, 40, 40, 0.3);
148
+ border-radius: 4px;
149
+ font-size: 10px;
150
+ font-weight: 600;
151
+ color: #c62828;
152
+ }
153
+
154
+ .errorSection {
155
+ margin-top: 12px;
156
+ padding-top: 12px;
157
+ border-top: 1px solid rgba(198, 40, 40, 0.3);
158
+ }
159
+
160
+ .errorSection strong {
161
+ display: block;
162
+ margin-bottom: 4px;
163
+ color: #c62828;
164
+ font-size: 11px;
165
+ }
166
+
167
+ .errorDataPre {
168
+ margin: 0;
169
+ padding: 8px;
170
+ background: rgba(198, 40, 40, 0.1);
171
+ border: 1px solid rgba(198, 40, 40, 0.3);
172
+ border-radius: 4px;
173
+ font-size: 10px;
174
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
175
+ overflow-x: auto;
176
+ max-height: 200px;
177
+ overflow-y: auto;
178
+ white-space: pre-wrap;
179
+ word-break: break-word;
180
+ color: #c62828;
181
+ }
182
+
183
+ .stderrOutput {
184
+ margin: 0;
185
+ padding: 8px;
186
+ background: rgba(255, 152, 0, 0.1);
187
+ border: 1px solid rgba(255, 152, 0, 0.3);
188
+ border-radius: 4px;
189
+ font-size: 10px;
190
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
191
+ overflow-x: auto;
192
+ max-height: 200px;
193
+ overflow-y: auto;
194
+ white-space: pre-wrap;
195
+ word-break: break-word;
196
+ color: #e65100;
197
+ }
198
+
199
+ .dataSection {
200
+ display: flex;
201
+ flex-direction: column;
202
+ gap: 12px;
203
+ margin-bottom: 12px;
204
+ }
205
+
206
+ .dataItem {
207
+ display: flex;
208
+ flex-direction: column;
209
+ gap: 4px;
210
+ }
211
+
212
+ .dataItem strong {
213
+ font-size: 12px;
214
+ color: #666;
215
+ text-transform: uppercase;
216
+ letter-spacing: 0.5px;
217
+ }
218
+
219
+ .jsonData {
220
+ margin: 0;
221
+ padding: 8px;
222
+ background: white;
223
+ border: 1px solid #e0e0e0;
224
+ border-radius: 4px;
225
+ font-size: 11px;
226
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
227
+ overflow-x: auto;
228
+ max-height: 200px;
229
+ overflow-y: auto;
230
+ white-space: pre-wrap;
231
+ word-break: break-word;
232
+ }
233
+
234
+ .errorOutput {
235
+ margin: 0;
236
+ padding: 8px;
237
+ background: #ffebee;
238
+ border: 1px solid #ef5350;
239
+ border-radius: 4px;
240
+ font-size: 11px;
241
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
242
+ overflow-x: auto;
243
+ max-height: 200px;
244
+ overflow-y: auto;
245
+ white-space: pre-wrap;
246
+ word-break: break-word;
247
+ color: #c62828;
248
+ }
249
+
250
+ .metadata {
251
+ display: flex;
252
+ gap: 16px;
253
+ font-size: 11px;
254
+ color: #666;
255
+ padding-top: 8px;
256
+ border-top: 1px solid #e0e0e0;
257
+ }
258
+
259
+ .metadata div {
260
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
261
+ }
262
+
263
+ /* Result item - styled to stand out at the bottom */
264
+ .resultItem {
265
+ border-top: 3px solid #4caf50;
266
+ background: linear-gradient(to bottom, #f1f8f4 0%, #ffffff 100%);
267
+ margin-top: auto;
268
+ }
269
+
270
+ .errorResultItem {
271
+ border-top: 3px solid #ef5350;
272
+ background: linear-gradient(to bottom, #ffebee 0%, #ffffff 100%);
273
+ }
274
+
275
+ .errorResultItem .resultHeader {
276
+ border-bottom: 2px solid #ef5350;
277
+ }
278
+
279
+ .resultHeader {
280
+ display: flex;
281
+ justify-content: space-between;
282
+ align-items: center;
283
+ padding: 16px;
284
+ border-bottom: 2px solid #4caf50;
285
+ flex-wrap: wrap;
286
+ gap: 12px;
287
+ }
288
+
289
+ .resultTitle {
290
+ display: flex;
291
+ align-items: center;
292
+ gap: 8px;
293
+ font-size: 14px;
294
+ color: #2e7d32;
295
+ }
296
+
297
+ .resultIcon {
298
+ font-size: 18px;
299
+ color: #4caf50;
300
+ font-weight: bold;
301
+ }
302
+
303
+ .errorIcon {
304
+ font-size: 18px;
305
+ color: #ef5350;
306
+ font-weight: bold;
307
+ }
308
+
309
+ .resultStats {
310
+ display: flex;
311
+ gap: 16px;
312
+ flex-wrap: wrap;
313
+ font-size: 12px;
314
+ }
315
+
316
+ .statItem {
317
+ color: #666;
318
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
319
+ }
320
+
321
+ .statItem strong {
322
+ color: #333;
323
+ margin-right: 4px;
324
+ }
325
+
326
+ .errorStat {
327
+ color: #d32f2f;
328
+ }
329
+
330
+ .resultContent {
331
+ padding: 16px;
332
+ background: white;
333
+ }
334
+
335
+ .resultPre {
336
+ margin: 0;
337
+ padding: 12px;
338
+ background: #fafafa;
339
+ border: 1px solid #e0e0e0;
340
+ border-radius: 4px;
341
+ font-size: 12px;
342
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
343
+ overflow-x: auto;
344
+ max-height: 300px;
345
+ overflow-y: auto;
346
+ white-space: pre-wrap;
347
+ word-break: break-word;
348
+ color: #333;
349
+ }
350
+
351
+ .errorResult {
352
+ padding: 16px;
353
+ background: white;
354
+ }
355
+
356
+ .errorPre {
357
+ margin: 0;
358
+ padding: 12px;
359
+ background: #ffebee;
360
+ border: 1px solid #ef5350;
361
+ border-radius: 4px;
362
+ font-size: 12px;
363
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
364
+ overflow-x: auto;
365
+ max-height: 300px;
366
+ overflow-y: auto;
367
+ white-space: pre-wrap;
368
+ word-break: break-word;
369
+ color: #c62828;
370
+ }
371
+