@imenam/self-register-proxy 1.0.42 → 1.0.45
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.
- package/README.md +74 -8
- package/config.js +40 -2
- package/package.json +1 -1
- package/proxy.js +39 -2
- package/routes/dashboard.js +1 -1
- package/routes/metaConfig.js +2 -2
- package/services/auth.js +20 -2
- package/services/portManager.js +59 -4
- package/views/dashboard.js +309 -803
- package/views/login.js +61 -130
- package/views/metaConfigs.js +403 -643
- package/views/terminal.html +272 -691
- package/views/terminal.js +34 -30
- package/views/theme.js +431 -0
package/views/terminal.html
CHANGED
|
@@ -3,386 +3,137 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>{{PROXY_NAME}}
|
|
6
|
+
<title>{{PROXY_NAME}} — Agent</title>
|
|
7
7
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm/css/xterm.css">
|
|
8
8
|
<style>
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
gap:
|
|
15
|
-
|
|
16
|
-
background: #1a1a2e;
|
|
17
|
-
border-bottom: 1px solid rgba(255,255,255,0.1);
|
|
18
|
-
color: #e4e4e7;
|
|
19
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
20
|
-
font-size: 0.85rem;
|
|
21
|
-
}
|
|
22
|
-
.back-link {
|
|
23
|
-
color: #8b5cf6;
|
|
24
|
-
text-decoration: none;
|
|
25
|
-
font-weight: 500;
|
|
26
|
-
}
|
|
27
|
-
.back-link:hover {
|
|
28
|
-
text-decoration: underline;
|
|
29
|
-
}
|
|
30
|
-
.terminal-title {
|
|
31
|
-
font-weight: 600;
|
|
32
|
-
color: #f4f4f5;
|
|
9
|
+
{{BASE_CSS}}
|
|
10
|
+
body { height: 100vh; display: flex; flex-direction: column; overflow: hidden; background: #0B0B0F; }
|
|
11
|
+
|
|
12
|
+
/* Barre supérieure */
|
|
13
|
+
.term-bar {
|
|
14
|
+
display: flex; align-items: center; gap: 16px; height: 48px; padding: 0 16px;
|
|
15
|
+
background: var(--bg); border-bottom: 1px solid var(--border); flex-shrink: 0;
|
|
33
16
|
}
|
|
17
|
+
.term-back { display: grid; place-items: center; width: 28px; height: 28px; border-radius: 6px; color: var(--text-2); }
|
|
18
|
+
.term-back:hover { background: var(--surface); color: var(--text); }
|
|
19
|
+
.term-title { font-size: 15px; font-weight: 600; }
|
|
34
20
|
.terminal-command {
|
|
35
|
-
color:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
flex-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
.
|
|
65
|
-
.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
.sidebar-expand-btn {
|
|
80
|
-
position: absolute; left: 0; top: 50%;
|
|
81
|
-
transform: translateY(-50%);
|
|
82
|
-
background: #12121f; border: 1px solid rgba(255,255,255,0.1);
|
|
83
|
-
border-left: none; border-radius: 0 6px 6px 0;
|
|
84
|
-
color: #71717a; cursor: pointer;
|
|
85
|
-
padding: 0.5rem 0.3rem; z-index: 10;
|
|
86
|
-
display: none; font-size: 1rem; line-height: 1;
|
|
87
|
-
}
|
|
88
|
-
.sidebar-expand-btn:hover { color: #e4e4e7; }
|
|
89
|
-
.sidebar-sync-warning {
|
|
90
|
-
font-size: 0.7rem; color: #fbbf24;
|
|
91
|
-
background: rgba(245,158,11,0.12);
|
|
92
|
-
padding: 0.3rem 0.75rem;
|
|
93
|
-
border-bottom: 1px solid rgba(245,158,11,0.3);
|
|
94
|
-
white-space: nowrap; overflow: hidden; flex-shrink: 0;
|
|
95
|
-
}
|
|
96
|
-
.sidebar-toolbar {
|
|
97
|
-
display: flex; gap: 0.4rem; padding: 0.5rem 0.6rem;
|
|
98
|
-
border-bottom: 1px solid rgba(255,255,255,0.06);
|
|
99
|
-
flex-shrink: 0;
|
|
100
|
-
}
|
|
101
|
-
.sb-btn {
|
|
102
|
-
border: none; border-radius: 5px; padding: 0.3rem 0.6rem;
|
|
103
|
-
font-size: 0.75rem; font-weight: 600; cursor: pointer; color: white;
|
|
104
|
-
transition: opacity 0.2s; white-space: nowrap;
|
|
105
|
-
}
|
|
106
|
-
.sb-btn:hover { opacity: 0.85; }
|
|
107
|
-
.sb-btn:disabled { opacity: 0.35; cursor: not-allowed; }
|
|
108
|
-
.sb-btn-primary { background: linear-gradient(90deg, #6366f1, #8b5cf6); }
|
|
109
|
-
.sb-btn-amber { background: linear-gradient(90deg, #f59e0b, #d97706); }
|
|
110
|
-
.sb-btn-green { background: linear-gradient(90deg, #10b981, #059669); }
|
|
111
|
-
.sb-btn-red { background: rgba(239,68,68,0.8); }
|
|
112
|
-
.sb-btn-ghost { background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.15); }
|
|
113
|
-
.sidebar-configs {
|
|
114
|
-
flex: 1; overflow-y: auto; padding: 0.5rem;
|
|
115
|
-
display: flex; flex-direction: column; gap: 0.4rem;
|
|
116
|
-
}
|
|
117
|
-
.sidebar-empty { font-size: 0.75rem; color: #52525b; text-align: center; padding: 1rem 0; }
|
|
118
|
-
.sb-config-card {
|
|
119
|
-
background: rgba(255,255,255,0.05);
|
|
120
|
-
border: 1px solid rgba(255,255,255,0.08);
|
|
121
|
-
border-left: 3px solid transparent;
|
|
122
|
-
border-radius: 6px; padding: 0.45rem 0.6rem;
|
|
123
|
-
}
|
|
124
|
-
.sb-config-card.active {
|
|
125
|
-
border-color: rgba(16,185,129,0.4);
|
|
126
|
-
border-left-color: #10b981;
|
|
127
|
-
background: rgba(16,185,129,0.08);
|
|
128
|
-
}
|
|
129
|
-
.sb-config-row {
|
|
130
|
-
display: flex; align-items: center; gap: 0.4rem;
|
|
131
|
-
}
|
|
132
|
-
.sb-config-name {
|
|
133
|
-
flex: 1; font-size: 0.8rem; font-weight: 600; color: #f4f4f5;
|
|
134
|
-
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
135
|
-
}
|
|
136
|
-
.sb-config-actions { display: flex; gap: 0.25rem; flex-shrink: 0; }
|
|
137
|
-
.sb-icon-btn {
|
|
138
|
-
border: none; border-radius: 4px; width: 24px; height: 24px;
|
|
139
|
-
display: inline-flex; align-items: center; justify-content: center;
|
|
140
|
-
font-size: 0.75rem; cursor: pointer; color: white; transition: opacity 0.2s;
|
|
141
|
-
flex-shrink: 0; padding: 0;
|
|
142
|
-
}
|
|
143
|
-
.sb-icon-btn:hover { opacity: 0.8; }
|
|
144
|
-
.sb-icon-btn:disabled { opacity: 0.3; cursor: not-allowed; }
|
|
145
|
-
.sb-icon-btn-green { background: linear-gradient(135deg, #10b981, #059669); }
|
|
146
|
-
.sb-icon-btn-ghost { background: rgba(255,255,255,0.12); border: 1px solid rgba(255,255,255,0.15); }
|
|
147
|
-
.sb-icon-btn-red { background: rgba(239,68,68,0.75); }
|
|
148
|
-
.sb-icon-btn-blue { background: rgba(99,102,241,0.75); }
|
|
149
|
-
.sb-icon-btn-amber { background: rgba(245,158,11,0.75); }
|
|
150
|
-
.sb-icon-btn-purple { background: linear-gradient(135deg, #6366f1, #8b5cf6); }
|
|
151
|
-
.sb-section-label {
|
|
152
|
-
font-size: 0.65rem; font-weight: 700; letter-spacing: 0.08em;
|
|
153
|
-
color: #52525b; text-transform: uppercase;
|
|
154
|
-
padding: 0.6rem 0.2rem 0.3rem; margin-top: 0.2rem;
|
|
155
|
-
border-top: 1px solid rgba(255,255,255,0.06);
|
|
156
|
-
display: flex; align-items: center; justify-content: space-between;
|
|
157
|
-
}
|
|
158
|
-
.sb-refresh-btn {
|
|
159
|
-
background: transparent; border: none; color: #52525b;
|
|
160
|
-
cursor: pointer; font-size: 0.85rem; line-height: 1; padding: 0.1rem;
|
|
161
|
-
transition: color 0.2s, transform 0.3s;
|
|
162
|
-
}
|
|
163
|
-
.sb-refresh-btn:hover { color: #a1a1aa; }
|
|
164
|
-
.sb-refresh-btn.spinning { animation: spin 0.6s linear infinite; }
|
|
165
|
-
.sb-service-card {
|
|
166
|
-
display: flex; align-items: center; gap: 0.4rem;
|
|
167
|
-
padding: 0.35rem 0.5rem;
|
|
168
|
-
background: rgba(255,255,255,0.03);
|
|
169
|
-
border: 1px solid rgba(255,255,255,0.06);
|
|
170
|
-
border-radius: 5px;
|
|
171
|
-
cursor: pointer;
|
|
172
|
-
transition: background 0.15s, border-color 0.15s;
|
|
173
|
-
}
|
|
174
|
-
.sb-service-card:hover {
|
|
175
|
-
background: rgba(255,255,255,0.06);
|
|
176
|
-
border-color: rgba(139,92,246,0.3);
|
|
177
|
-
}
|
|
178
|
-
.sb-service-name {
|
|
179
|
-
flex: 1; font-size: 0.78rem; font-weight: 500; color: #d4d4d8;
|
|
180
|
-
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
181
|
-
}
|
|
182
|
-
.sb-service-actions { display: flex; gap: 0.25rem; flex-shrink: 0; }
|
|
183
|
-
/* Modal méta-config (partagée) */
|
|
184
|
-
.sb-modal-overlay {
|
|
185
|
-
display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0;
|
|
186
|
-
background: rgba(0,0,0,0.6);
|
|
187
|
-
z-index: 1000; align-items: center; justify-content: center;
|
|
188
|
-
}
|
|
189
|
-
.sb-modal-overlay.active { display: flex; }
|
|
190
|
-
.sb-modal {
|
|
191
|
-
background: linear-gradient(135deg, #1e1e3f 0%, #1a1a2e 100%);
|
|
192
|
-
border: 1px solid rgba(255,255,255,0.15); border-radius: 12px;
|
|
193
|
-
padding: 1.5rem; width: 90%; max-width: 480px;
|
|
194
|
-
box-shadow: 0 20px 40px rgba(0,0,0,0.4); max-height: 90vh; overflow-y: auto;
|
|
195
|
-
}
|
|
196
|
-
.sb-modal-header {
|
|
197
|
-
display: flex; justify-content: space-between; align-items: center;
|
|
198
|
-
margin-bottom: 1rem;
|
|
199
|
-
}
|
|
200
|
-
.sb-modal-header h2 { font-size: 1.1rem; color: #f4f4f5; }
|
|
201
|
-
.sb-modal-close {
|
|
202
|
-
background: transparent; border: none; color: #71717a;
|
|
203
|
-
font-size: 1.25rem; cursor: pointer; padding: 0.25rem; line-height: 1;
|
|
204
|
-
}
|
|
205
|
-
.sb-modal-close:hover { color: #e4e4e7; }
|
|
206
|
-
.sb-form-group { margin-bottom: 0.75rem; }
|
|
207
|
-
.sb-form-group label { font-size: 0.75rem; color: #a1a1aa; margin-bottom: 0.25rem; display: block; }
|
|
208
|
-
.sb-form-group input, .sb-form-group textarea {
|
|
209
|
-
background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.1);
|
|
210
|
-
padding: 0.5rem 0.75rem; border-radius: 6px; color: white;
|
|
211
|
-
font-family: inherit; font-size: 0.9rem; width: 100%;
|
|
212
|
-
}
|
|
213
|
-
.sb-form-group textarea { resize: vertical; min-height: 60px; }
|
|
214
|
-
.sb-form-group input:focus, .sb-form-group textarea:focus { outline: none; border-color: #8b5cf6; }
|
|
215
|
-
.sb-modal-mappings { margin-top: 0.75rem; }
|
|
216
|
-
.sb-modal-mappings-title { font-size: 0.8rem; font-weight: 600; color: #a1a1aa; margin-bottom: 0.5rem; }
|
|
217
|
-
.sb-modal-mappings-list { display: flex; flex-direction: column; gap: 0.4rem; }
|
|
218
|
-
.sb-modal-mapping-item {
|
|
219
|
-
display: flex; align-items: center; gap: 0.5rem;
|
|
220
|
-
background: rgba(0,0,0,0.2); padding: 0.4rem 0.6rem; border-radius: 6px;
|
|
221
|
-
}
|
|
222
|
-
.sb-modal-mapping-item .mcp-label { flex: 1; font-size: 0.8rem; color: #f4f4f5; font-weight: 500; }
|
|
223
|
-
.sb-modal-mapping-item .mcp-path-label { font-size: 0.7rem; color: #8b5cf6; font-family: 'SF Mono', 'Fira Code', monospace; }
|
|
224
|
-
.sb-modal-mapping-item select {
|
|
225
|
-
background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.15);
|
|
226
|
-
color: #e4e4e7; padding: 0.3rem 0.5rem; border-radius: 4px; font-size: 0.8rem; min-width: 130px;
|
|
227
|
-
}
|
|
228
|
-
.sb-modal-mapping-item select:focus { outline: none; border-color: #8b5cf6; }
|
|
229
|
-
.sb-spinner {
|
|
230
|
-
display: inline-block; width: 12px; height: 12px;
|
|
231
|
-
border: 2px solid rgba(255,255,255,0.2); border-top-color: #8b5cf6;
|
|
232
|
-
border-radius: 50%; animation: spin 0.6s linear infinite;
|
|
233
|
-
}
|
|
234
|
-
.prompt-btn {
|
|
235
|
-
background: linear-gradient(90deg, #6366f1, #8b5cf6);
|
|
236
|
-
color: white;
|
|
237
|
-
border: none;
|
|
238
|
-
padding: 0.4rem 0.8rem;
|
|
239
|
-
border-radius: 6px;
|
|
240
|
-
font-weight: 600;
|
|
241
|
-
font-size: 0.8rem;
|
|
242
|
-
cursor: pointer;
|
|
243
|
-
transition: opacity 0.2s;
|
|
244
|
-
}
|
|
245
|
-
.prompt-btn:hover { opacity: 0.9; }
|
|
246
|
-
.modal-overlay {
|
|
247
|
-
display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0;
|
|
248
|
-
background: rgba(0,0,0,0.6);
|
|
249
|
-
z-index: 1000; align-items: center; justify-content: center;
|
|
250
|
-
}
|
|
251
|
-
.modal-overlay.active { display: flex; }
|
|
252
|
-
.modal {
|
|
253
|
-
background: linear-gradient(135deg, #1e1e3f 0%, #1a1a2e 100%);
|
|
254
|
-
border: 1px solid rgba(255,255,255,0.15); border-radius: 12px;
|
|
255
|
-
padding: 1.5rem; width: 90%; max-width: 640px;
|
|
256
|
-
box-shadow: 0 20px 40px rgba(0,0,0,0.4);
|
|
257
|
-
display: flex; flex-direction: column; gap: 1rem;
|
|
258
|
-
}
|
|
259
|
-
.modal-header {
|
|
260
|
-
display: flex; justify-content: space-between; align-items: center;
|
|
261
|
-
}
|
|
262
|
-
.modal-header h2 { font-size: 1.1rem; color: #f4f4f5; }
|
|
263
|
-
.modal-close {
|
|
264
|
-
background: transparent; border: none; color: #71717a;
|
|
265
|
-
font-size: 1.25rem; cursor: pointer; padding: 0.25rem; line-height: 1;
|
|
266
|
-
}
|
|
267
|
-
.modal-close:hover { color: #e4e4e7; }
|
|
268
|
-
#promptTextarea {
|
|
269
|
-
background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.1);
|
|
270
|
-
border-radius: 6px; color: white; font-family: inherit; font-size: 0.9rem;
|
|
271
|
-
padding: 0.75rem; width: 100%; min-height: 200px; resize: vertical; line-height: 1.5;
|
|
272
|
-
}
|
|
273
|
-
#promptTextarea:focus { outline: none; border-color: #8b5cf6; }
|
|
274
|
-
.modal-actions { display: flex; justify-content: flex-end; gap: 0.5rem; }
|
|
275
|
-
.btn-cancel {
|
|
276
|
-
background: rgba(255,255,255,0.08); color: #e4e4e7; border: none;
|
|
277
|
-
padding: 0.5rem 1rem; border-radius: 6px; font-size: 0.85rem; cursor: pointer;
|
|
278
|
-
}
|
|
279
|
-
.btn-send {
|
|
280
|
-
background: linear-gradient(90deg, #6366f1, #8b5cf6); color: white; border: none;
|
|
281
|
-
padding: 0.5rem 1rem; border-radius: 6px; font-weight: 600; font-size: 0.85rem; cursor: pointer;
|
|
282
|
-
}
|
|
21
|
+
font-family: var(--mono); font-size: 12px; color: var(--text-3);
|
|
22
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 320px;
|
|
23
|
+
}
|
|
24
|
+
.term-spacer { flex: 1 1 auto; }
|
|
25
|
+
.term-select {
|
|
26
|
+
display: flex; align-items: center; gap: 10px; height: 30px; padding: 0 10px 0 12px;
|
|
27
|
+
background: var(--surface); border: 1px solid var(--border); border-radius: 8px;
|
|
28
|
+
color: var(--text); font-family: inherit; font-size: 13px; cursor: pointer;
|
|
29
|
+
}
|
|
30
|
+
.term-select:hover { border-color: #3A3A45; }
|
|
31
|
+
.term-select svg { color: var(--text-3); }
|
|
32
|
+
.term-select-name { max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
33
|
+
.term-actions { display: flex; align-items: center; gap: 12px; }
|
|
34
|
+
.term-bar .btn { height: 30px; }
|
|
35
|
+
.menu-scroll { max-height: 320px; overflow-y: auto; }
|
|
36
|
+
.menu-empty { font-size: 12px; color: var(--text-3); padding: 8px; }
|
|
37
|
+
|
|
38
|
+
/* Terminal */
|
|
39
|
+
.term-body { flex: 1 1 auto; min-height: 0; padding: 12px 16px 0; }
|
|
40
|
+
#terminal-container { width: 100%; height: 100%; }
|
|
41
|
+
.xterm-viewport::-webkit-scrollbar { width: 10px; }
|
|
42
|
+
.xterm-viewport::-webkit-scrollbar-thumb { background: #2A2A33; border-radius: 5px; }
|
|
43
|
+
|
|
44
|
+
/* Barre de saisie */
|
|
45
|
+
.term-input {
|
|
46
|
+
flex-shrink: 0; margin: 12px 16px 16px; padding: 8px 8px 8px 16px;
|
|
47
|
+
display: flex; align-items: flex-end; gap: 12px;
|
|
48
|
+
background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
|
|
49
|
+
}
|
|
50
|
+
.term-input:focus-within { border-color: #3A3A45; }
|
|
51
|
+
.term-input textarea {
|
|
52
|
+
flex: 1 1 auto; background: none; border: 0; padding: 8px 0; resize: none;
|
|
53
|
+
color: var(--text); font-family: inherit; font-size: 14px; line-height: 1.5;
|
|
54
|
+
max-height: 160px; overflow-y: auto;
|
|
55
|
+
}
|
|
56
|
+
.term-input textarea:focus { outline: none; border: 0; }
|
|
57
|
+
.term-hint { font-size: 12px; color: var(--text-4); padding-bottom: 12px; white-space: nowrap; }
|
|
58
|
+
.term-send {
|
|
59
|
+
flex-shrink: 0; width: 36px; height: 36px; border-radius: 10px; border: 0;
|
|
60
|
+
background: var(--accent); color: #fff; display: grid; place-items: center; cursor: pointer;
|
|
61
|
+
}
|
|
62
|
+
.term-send:hover { background: #5457E5; }
|
|
63
|
+
|
|
64
|
+
/* Autocomplete @ */
|
|
283
65
|
.file-dropdown {
|
|
284
|
-
display: none;
|
|
285
|
-
background:
|
|
286
|
-
|
|
287
|
-
box-shadow: 0 8px 24px rgba(0,0,0,0.4);
|
|
66
|
+
position: fixed; z-index: 200; display: none; max-height: 260px; overflow-y: auto;
|
|
67
|
+
background: var(--surface-2); border: 1px solid #2E2E38; border-radius: 10px; padding: 6px;
|
|
68
|
+
box-shadow: 0 16px 40px rgba(0,0,0,.55);
|
|
288
69
|
}
|
|
289
70
|
.file-dropdown-item {
|
|
290
|
-
padding:
|
|
291
|
-
|
|
292
|
-
font-family: 'SF Mono', 'Fira Code', monospace;
|
|
293
|
-
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
71
|
+
padding: 6px 10px; border-radius: 6px; font-size: 13px; color: var(--text-2);
|
|
72
|
+
cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
294
73
|
}
|
|
295
|
-
.file-dropdown-item.active { background:
|
|
296
|
-
.file-dropdown-item:hover { background: rgba(255,255,255,0.08); }
|
|
74
|
+
.file-dropdown-item:hover, .file-dropdown-item.active { background: #26262E; color: var(--text); }
|
|
297
75
|
.file-path-hint {
|
|
298
|
-
|
|
299
|
-
background:
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
76
|
+
position: fixed; z-index: 201; display: none; padding: 5px 10px;
|
|
77
|
+
background: var(--surface-2); border: 1px solid #2E2E38; border-radius: 6px;
|
|
78
|
+
font-family: var(--mono); font-size: 11px; color: var(--text-3); pointer-events: none;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@media (max-width: 760px) {
|
|
82
|
+
.terminal-command, .term-hint { display: none; }
|
|
83
|
+
.term-bar { gap: 10px; padding: 0 10px; }
|
|
84
|
+
.term-select-name { max-width: 90px; }
|
|
304
85
|
}
|
|
305
86
|
</style>
|
|
306
87
|
</head>
|
|
307
88
|
<body>
|
|
308
|
-
<
|
|
309
|
-
<a href="/"
|
|
310
|
-
|
|
311
|
-
|
|
89
|
+
<header class="term-bar">
|
|
90
|
+
<a class="term-back" href="/" title="Retour au dashboard" aria-label="Retour au dashboard">
|
|
91
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12 19-7-7 7-7"/><path d="M19 12H5"/></svg>
|
|
92
|
+
</a>
|
|
93
|
+
<span class="term-title">Agent</span>
|
|
312
94
|
{{TERMINAL_COMMAND}}
|
|
313
|
-
</div>
|
|
314
|
-
<div class="modal-overlay" id="promptModal" onclick="if(event.target===this)closePromptModal()">
|
|
315
|
-
<div class="modal">
|
|
316
|
-
<div class="modal-header">
|
|
317
|
-
<h2>Saisir un prompt</h2>
|
|
318
|
-
<button class="modal-close" onclick="closePromptModal()">×</button>
|
|
319
|
-
</div>
|
|
320
|
-
<textarea id="promptTextarea" placeholder="Saisissez votre prompt…"></textarea>
|
|
321
|
-
<div class="modal-actions">
|
|
322
|
-
<button class="btn-cancel" onclick="closePromptModal()">Annuler</button>
|
|
323
|
-
<button class="btn-send" onclick="sendPrompt()">Envoyer</button>
|
|
324
|
-
</div>
|
|
325
|
-
</div>
|
|
326
|
-
</div>
|
|
327
|
-
<div id="fileDropdown" class="file-dropdown"></div>
|
|
328
|
-
<div id="filePathHint" class="file-path-hint"></div>
|
|
329
95
|
|
|
330
|
-
|
|
331
|
-
<aside class="meta-sidebar" id="metaSidebar">
|
|
332
|
-
<div class="sidebar-header">
|
|
333
|
-
<span class="sidebar-title">Méta-configs</span>
|
|
334
|
-
<button class="sidebar-collapse-btn" onclick="toggleSidebar()" title="Réduire">‹</button>
|
|
335
|
-
</div>
|
|
336
|
-
<div class="sidebar-sync-warning" id="sidebarSyncWarning" style="display:none;">⚠ Désynchronisé</div>
|
|
337
|
-
<div class="sidebar-toolbar">
|
|
338
|
-
<button class="sb-btn sb-btn-amber" id="sbDiscoverBtn" onclick="sidebarDiscover()">🔍</button>
|
|
339
|
-
<button class="sb-btn sb-btn-primary" id="sbCreateBtn" onclick="openSbModal()" disabled>+</button>
|
|
340
|
-
</div>
|
|
341
|
-
<div class="sidebar-configs">
|
|
342
|
-
<div id="sidebarConfigs"><p class="sidebar-empty">Chargement…</p></div>
|
|
343
|
-
<div class="sb-section-label" id="sbServicesLabel">
|
|
344
|
-
Services
|
|
345
|
-
<button class="sb-refresh-btn" id="sbRefreshRoutesBtn" onclick="refreshSidebarRoutes()" title="Rafraîchir">↻</button>
|
|
346
|
-
</div>
|
|
347
|
-
<div id="sidebarServices" style="display:flex;flex-direction:column;gap:0.3rem;"></div>
|
|
348
|
-
</div>
|
|
349
|
-
</aside>
|
|
96
|
+
<span class="term-spacer"></span>
|
|
350
97
|
|
|
351
|
-
<
|
|
98
|
+
<div class="menu-wrap">
|
|
99
|
+
<button class="term-select" onclick="toggleMenu(this)" id="profileBtn">
|
|
100
|
+
<span class="dot" id="profileDot"></span>
|
|
101
|
+
<span class="term-select-name" id="profileName">Aucun profil</span>
|
|
102
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
|
|
103
|
+
</button>
|
|
104
|
+
<div class="menu" id="profileMenu"><div class="menu-empty">Chargement…</div></div>
|
|
105
|
+
</div>
|
|
352
106
|
|
|
353
|
-
<
|
|
354
|
-
</div>
|
|
107
|
+
<span class="term-spacer"></span>
|
|
355
108
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
109
|
+
<div class="term-actions">
|
|
110
|
+
<div class="menu-wrap">
|
|
111
|
+
<button class="term-select" onclick="toggleServicesMenu(this)">
|
|
112
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z"/><path d="m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65"/><path d="m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65"/></svg>
|
|
113
|
+
<span>Services</span>
|
|
114
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
|
|
115
|
+
</button>
|
|
116
|
+
<div class="menu menu-scroll" id="servicesMenu"><div class="menu-empty">Chargement…</div></div>
|
|
361
117
|
</div>
|
|
362
|
-
<
|
|
363
|
-
<
|
|
364
|
-
<
|
|
365
|
-
|
|
366
|
-
<input type="text" id="sbCfgId" placeholder="mon-profil" required pattern="[a-z0-9\-]+" title="Lettres minuscules, chiffres et tirets uniquement">
|
|
367
|
-
</div>
|
|
368
|
-
<div class="sb-form-group">
|
|
369
|
-
<label for="sbCfgName">Nom</label>
|
|
370
|
-
<input type="text" id="sbCfgName" placeholder="Mon Profil" required>
|
|
371
|
-
</div>
|
|
372
|
-
<div class="sb-form-group">
|
|
373
|
-
<label for="sbCfgDesc">Description</label>
|
|
374
|
-
<textarea id="sbCfgDesc" placeholder="Description optionnelle..."></textarea>
|
|
375
|
-
</div>
|
|
376
|
-
<div class="sb-modal-mappings">
|
|
377
|
-
<div class="sb-modal-mappings-title">Mappings MCP → Configuration</div>
|
|
378
|
-
<div class="sb-modal-mappings-list" id="sbModalMappings">
|
|
379
|
-
<p style="font-size:0.75rem;color:#71717a;">Lancez d'abord la découverte des MCPs.</p>
|
|
380
|
-
</div>
|
|
381
|
-
</div>
|
|
382
|
-
<button type="submit" class="sb-btn sb-btn-primary" style="width:100%;margin-top:1rem;" id="sbModalSubmitBtn">Créer</button>
|
|
383
|
-
</form>
|
|
118
|
+
<button class="btn btn-primary" onclick="focusPrompt()">
|
|
119
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/><path d="m15 5 4 4"/></svg>
|
|
120
|
+
<span>Prompt</span>
|
|
121
|
+
</button>
|
|
384
122
|
</div>
|
|
385
|
-
</
|
|
123
|
+
</header>
|
|
124
|
+
|
|
125
|
+
<main class="term-body"><div id="terminal-container"></div></main>
|
|
126
|
+
|
|
127
|
+
<footer class="term-input">
|
|
128
|
+
<textarea id="promptTextarea" rows="1" placeholder="Écrire un prompt…"></textarea>
|
|
129
|
+
<span class="term-hint">Ctrl+⏎ pour envoyer</span>
|
|
130
|
+
<button class="term-send" onclick="sendPrompt()" title="Envoyer" aria-label="Envoyer">
|
|
131
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m5 12 7-7 7 7"/><path d="M12 19V5"/></svg>
|
|
132
|
+
</button>
|
|
133
|
+
</footer>
|
|
134
|
+
|
|
135
|
+
<div id="fileDropdown" class="file-dropdown"></div>
|
|
136
|
+
<div id="filePathHint" class="file-path-hint"></div>
|
|
386
137
|
|
|
387
138
|
<script src="https://cdn.jsdelivr.net/npm/xterm/lib/xterm.min.js"></script>
|
|
388
139
|
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit/lib/xterm-addon-fit.min.js"></script>
|
|
@@ -396,9 +147,13 @@
|
|
|
396
147
|
...options,
|
|
397
148
|
terminalOptions: {
|
|
398
149
|
cursorBlink: true,
|
|
150
|
+
fontSize: 13,
|
|
151
|
+
fontFamily: 'ui-monospace, SFMono-Regular, Consolas, Menlo, monospace',
|
|
399
152
|
theme: {
|
|
400
|
-
background: '#
|
|
401
|
-
foreground: '#
|
|
153
|
+
background: '#0B0B0F',
|
|
154
|
+
foreground: '#D4D4D8',
|
|
155
|
+
cursor: '#818CF8',
|
|
156
|
+
selectionBackground: 'rgba(99,102,241,.35)',
|
|
402
157
|
},
|
|
403
158
|
...options.terminalOptions,
|
|
404
159
|
},
|
|
@@ -498,7 +253,54 @@
|
|
|
498
253
|
wsPath: '/terminal-ws',
|
|
499
254
|
});
|
|
500
255
|
|
|
501
|
-
|
|
256
|
+
function esc(str) {
|
|
257
|
+
const d = document.createElement('div');
|
|
258
|
+
d.textContent = String(str == null ? '' : str);
|
|
259
|
+
return d.innerHTML;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/* --- Menus : un seul ouvert à la fois --- */
|
|
263
|
+
function toggleMenu(btn) {
|
|
264
|
+
const menu = btn.nextElementSibling;
|
|
265
|
+
const wasOpen = menu.classList.contains('open');
|
|
266
|
+
closeAllMenus();
|
|
267
|
+
if (!wasOpen) menu.classList.add('open');
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function closeAllMenus() {
|
|
271
|
+
document.querySelectorAll('.menu.open').forEach(m => m.classList.remove('open'));
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
document.addEventListener('click', (e) => {
|
|
275
|
+
if (!e.target.closest('.menu-wrap')) closeAllMenus();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
/* --- Barre de saisie du prompt --- */
|
|
279
|
+
const _ta = document.getElementById('promptTextarea');
|
|
280
|
+
|
|
281
|
+
function focusPrompt() {
|
|
282
|
+
_ta.focus();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function autoGrow() {
|
|
286
|
+
_ta.style.height = 'auto';
|
|
287
|
+
_ta.style.height = Math.min(_ta.scrollHeight, 160) + 'px';
|
|
288
|
+
term.fit();
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
_ta.addEventListener('input', autoGrow);
|
|
292
|
+
|
|
293
|
+
function sendPrompt() {
|
|
294
|
+
const text = _ta.value;
|
|
295
|
+
if (!text.trim()) return;
|
|
296
|
+
term.ws.send(text);
|
|
297
|
+
_ta.value = '';
|
|
298
|
+
autoGrow();
|
|
299
|
+
_acHide();
|
|
300
|
+
term.terminal.focus();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/* --- Autocomplete @ (buffer-based) --- */
|
|
502
304
|
let _acBuffer = null;
|
|
503
305
|
let _acFiltered = [];
|
|
504
306
|
let _acAtPos = -1;
|
|
@@ -523,10 +325,9 @@
|
|
|
523
325
|
_acFiltered = files;
|
|
524
326
|
_acIdx = -1;
|
|
525
327
|
const el = document.getElementById('fileDropdown');
|
|
526
|
-
const ta = document.getElementById('promptTextarea');
|
|
527
328
|
el.innerHTML = '';
|
|
528
329
|
if (!files.length) { el.style.display = 'none'; _acHidePathHint(); return; }
|
|
529
|
-
files.forEach((file
|
|
330
|
+
files.forEach((file) => {
|
|
530
331
|
const d = document.createElement('div');
|
|
531
332
|
d.className = 'file-dropdown-item';
|
|
532
333
|
d.textContent = file.split('/').pop();
|
|
@@ -535,11 +336,12 @@
|
|
|
535
336
|
d.addEventListener('mousedown', ev => { ev.preventDefault(); _acInsert(file); });
|
|
536
337
|
el.appendChild(d);
|
|
537
338
|
});
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
el.style.top = (rect.bottom + 4) + 'px';
|
|
541
|
-
el.style.width = Math.min(rect.width, 300) + 'px';
|
|
339
|
+
// Le dropdown s'ouvre vers le haut : la saisie est en bas de l'écran.
|
|
340
|
+
const rect = _ta.getBoundingClientRect();
|
|
542
341
|
el.style.display = 'block';
|
|
342
|
+
el.style.left = rect.left + 'px';
|
|
343
|
+
el.style.width = Math.min(rect.width, 320) + 'px';
|
|
344
|
+
el.style.top = Math.max(8, rect.top - el.offsetHeight - 6) + 'px';
|
|
543
345
|
}
|
|
544
346
|
|
|
545
347
|
function _acHide() {
|
|
@@ -571,44 +373,18 @@
|
|
|
571
373
|
}
|
|
572
374
|
|
|
573
375
|
function _acInsert(file) {
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
ta.setSelectionRange(_acAtPos + 1 + file.length, _acAtPos + 1 + file.length);
|
|
578
|
-
_acHide();
|
|
579
|
-
ta.focus();
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
function openPromptModal() {
|
|
583
|
-
document.getElementById('promptModal').classList.add('active');
|
|
584
|
-
document.getElementById('promptTextarea').focus();
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
function closePromptModal() {
|
|
376
|
+
const end = _ta.selectionStart;
|
|
377
|
+
_ta.value = _ta.value.slice(0, _acAtPos) + '@' + file + _ta.value.slice(end);
|
|
378
|
+
_ta.setSelectionRange(_acAtPos + 1 + file.length, _acAtPos + 1 + file.length);
|
|
588
379
|
_acHide();
|
|
589
|
-
|
|
590
|
-
document.getElementById('promptTextarea').value = '';
|
|
380
|
+
_ta.focus();
|
|
591
381
|
}
|
|
592
382
|
|
|
593
|
-
function sendPrompt() {
|
|
594
|
-
const text = document.getElementById('promptTextarea').value;
|
|
595
|
-
if (!text) return;
|
|
596
|
-
term.ws.send(text);
|
|
597
|
-
closePromptModal();
|
|
598
|
-
term.terminal.focus();
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
document.addEventListener('keydown', (e) => {
|
|
602
|
-
if (e.key === 'Escape') { closePromptModal(); closeSbModal(); }
|
|
603
|
-
if (e.ctrlKey && e.shiftKey && e.key === 'P') { e.preventDefault(); openPromptModal(); }
|
|
604
|
-
});
|
|
605
|
-
|
|
606
|
-
const _ta = document.getElementById('promptTextarea');
|
|
607
|
-
|
|
608
383
|
_ta.addEventListener('keydown', e => {
|
|
609
|
-
|
|
384
|
+
// Ctrl+Entrée envoie ; Entrée seule passe à la ligne.
|
|
385
|
+
if (e.ctrlKey && e.key === 'Enter') { e.preventDefault(); sendPrompt(); return; }
|
|
610
386
|
|
|
611
|
-
const dropdownVisible = document.getElementById('fileDropdown').style.display
|
|
387
|
+
const dropdownVisible = document.getElementById('fileDropdown').style.display === 'block';
|
|
612
388
|
|
|
613
389
|
if (dropdownVisible) {
|
|
614
390
|
const items = [...document.getElementById('fileDropdown').querySelectorAll('.file-dropdown-item')];
|
|
@@ -630,6 +406,7 @@
|
|
|
630
406
|
return;
|
|
631
407
|
}
|
|
632
408
|
if (e.key === 'Escape') {
|
|
409
|
+
e.preventDefault();
|
|
633
410
|
_acHide();
|
|
634
411
|
return;
|
|
635
412
|
}
|
|
@@ -659,333 +436,137 @@
|
|
|
659
436
|
}
|
|
660
437
|
});
|
|
661
438
|
|
|
662
|
-
_ta.addEventListener('blur', () => {
|
|
663
|
-
setTimeout(() => _acHide(), 150);
|
|
664
|
-
});
|
|
439
|
+
_ta.addEventListener('blur', () => { setTimeout(() => _acHide(), 150); });
|
|
665
440
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
let sbOfflineMappings = {};
|
|
671
|
-
let sbActiveId = null;
|
|
672
|
-
let sbSyncInterval = null;
|
|
673
|
-
let sbSynced = true;
|
|
441
|
+
document.addEventListener('keydown', (e) => {
|
|
442
|
+
if (e.key === 'Escape') { closeAllMenus(); }
|
|
443
|
+
if (e.ctrlKey && e.shiftKey && e.key === 'P') { e.preventDefault(); focusPrompt(); }
|
|
444
|
+
});
|
|
674
445
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
446
|
+
/* --- Profils de méta-configuration --- */
|
|
447
|
+
let profiles = {};
|
|
448
|
+
let activeProfileId = null;
|
|
449
|
+
let profilesSynced = true;
|
|
450
|
+
let syncInterval = null;
|
|
680
451
|
|
|
681
|
-
function
|
|
682
|
-
const sidebar = document.getElementById('metaSidebar');
|
|
683
|
-
const expandBtn = document.getElementById('sidebarExpandBtn');
|
|
684
|
-
const collapsed = sidebar.classList.toggle('collapsed');
|
|
685
|
-
expandBtn.style.display = collapsed ? '' : 'none';
|
|
686
|
-
setTimeout(() => term.fit(), 210);
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
async function loadSidebarConfigs() {
|
|
452
|
+
async function loadProfiles() {
|
|
690
453
|
try {
|
|
691
454
|
const res = await fetch('/proxy/meta-configs');
|
|
692
455
|
const data = await res.json();
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
} catch (err) {
|
|
701
|
-
document.getElementById('sidebarConfigs').innerHTML = '<p class="sidebar-empty">Erreur de chargement</p>';
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
function renderSidebarConfigs(configs, activeId) {
|
|
706
|
-
const el = document.getElementById('sidebarConfigs');
|
|
707
|
-
const entries = Object.entries(configs);
|
|
708
|
-
if (!entries.length) {
|
|
709
|
-
el.innerHTML = '<p class="sidebar-empty">Aucun profil</p>';
|
|
710
|
-
return;
|
|
711
|
-
}
|
|
712
|
-
el.innerHTML = entries.map(([id, cfg]) => {
|
|
713
|
-
const isActive = id === activeId;
|
|
714
|
-
return '<div class="sb-config-card' + (isActive ? ' active' : '') + '" data-id="' + sbEsc(id) + '">'
|
|
715
|
-
+ '<div class="sb-config-row">'
|
|
716
|
-
+ '<span class="sb-config-name">' + sbEsc(cfg.name) + '</span>'
|
|
717
|
-
+ '<div class="sb-config-actions">'
|
|
718
|
-
+ '<button class="sb-icon-btn sb-icon-btn-green" data-id="' + sbEsc(id) + '" onclick="sidebarApply(this.dataset.id, this)" title="Appliquer">✓</button>'
|
|
719
|
-
+ '<button class="sb-icon-btn sb-icon-btn-ghost" data-id="' + sbEsc(id) + '" onclick="sidebarEdit(this.dataset.id)" title="Modifier">✎</button>'
|
|
720
|
-
+ '<button class="sb-icon-btn sb-icon-btn-red" data-id="' + sbEsc(id) + '" onclick="sidebarDelete(this.dataset.id)" title="Supprimer">✕</button>'
|
|
721
|
-
+ '</div></div></div>';
|
|
722
|
-
}).join('');
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
async function sidebarApply(id, btn) {
|
|
726
|
-
if (btn.disabled) return;
|
|
727
|
-
btn.disabled = true;
|
|
728
|
-
btn.innerHTML = '<span class="sb-spinner"></span>';
|
|
729
|
-
try {
|
|
730
|
-
const res = await fetch('/proxy/meta-configs/' + encodeURIComponent(id) + '/apply', { method: 'POST' });
|
|
731
|
-
const data = await res.json();
|
|
732
|
-
if (data.success) {
|
|
733
|
-
sbActiveId = id;
|
|
734
|
-
sbSynced = true;
|
|
735
|
-
document.getElementById('sidebarSyncWarning').style.display = 'none';
|
|
736
|
-
if (!sbSyncInterval) {
|
|
737
|
-
sbCheckSync();
|
|
738
|
-
sbSyncInterval = setInterval(sbCheckSync, 30000);
|
|
739
|
-
}
|
|
740
|
-
await loadSidebarConfigs();
|
|
741
|
-
} else {
|
|
742
|
-
alert('Erreur lors de l\u2019application du profil.');
|
|
743
|
-
}
|
|
744
|
-
} catch (err) {
|
|
745
|
-
alert('Erreur r\u00e9seau: ' + err.message);
|
|
746
|
-
} finally {
|
|
747
|
-
btn.disabled = false;
|
|
748
|
-
btn.innerHTML = '✓';
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
async function sidebarDelete(id) {
|
|
753
|
-
if (!confirm('Supprimer ce profil ?')) return;
|
|
754
|
-
try {
|
|
755
|
-
const res = await fetch('/proxy/meta-configs/' + encodeURIComponent(id), { method: 'DELETE' });
|
|
756
|
-
if (res.ok) {
|
|
757
|
-
await loadSidebarConfigs();
|
|
758
|
-
} else {
|
|
759
|
-
const data = await res.json();
|
|
760
|
-
alert('Erreur: ' + (data.error || 'Inconnue'));
|
|
761
|
-
}
|
|
762
|
-
} catch (err) {
|
|
763
|
-
alert('Erreur r\u00e9seau: ' + err.message);
|
|
456
|
+
profiles = data.metaConfigs || {};
|
|
457
|
+
activeProfileId = data.activeMetaConfig || null;
|
|
458
|
+
renderProfileSelect();
|
|
459
|
+
renderProfileMenu();
|
|
460
|
+
startSyncPolling();
|
|
461
|
+
} catch (_) {
|
|
462
|
+
document.getElementById('profileMenu').innerHTML = '<div class="menu-empty">Erreur de chargement</div>';
|
|
764
463
|
}
|
|
765
464
|
}
|
|
766
465
|
|
|
767
|
-
|
|
768
|
-
const
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
466
|
+
function renderProfileSelect() {
|
|
467
|
+
const dot = document.getElementById('profileDot');
|
|
468
|
+
const name = document.getElementById('profileName');
|
|
469
|
+
const active = activeProfileId && profiles[activeProfileId];
|
|
470
|
+
name.textContent = active ? active.name : 'Aucun profil';
|
|
471
|
+
dot.className = 'dot' + (!active ? '' : (profilesSynced ? ' up' : ' warn'));
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function renderProfileMenu() {
|
|
475
|
+
const menu = document.getElementById('profileMenu');
|
|
476
|
+
const entries = Object.entries(profiles);
|
|
477
|
+
let html = entries.length
|
|
478
|
+
? entries.map(([id, cfg]) =>
|
|
479
|
+
'<button class="menu-item' + (id === activeProfileId ? ' active' : '') + '" onclick="applyProfile(\'' + esc(id) + '\')">'
|
|
480
|
+
+ (id === activeProfileId
|
|
481
|
+
? '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>'
|
|
482
|
+
: '<span style="width:16px;"></span>')
|
|
483
|
+
+ '<span>' + esc(cfg.name) + '</span></button>').join('')
|
|
484
|
+
: '<div class="menu-empty">Aucun profil</div>';
|
|
485
|
+
|
|
486
|
+
html += '<div class="menu-sep"></div>'
|
|
487
|
+
+ '<a class="menu-item" href="/proxy/meta-configs-ui">'
|
|
488
|
+
+ '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="21" x2="14" y1="4" y2="4"/><line x1="10" x2="3" y1="4" y2="4"/><line x1="21" x2="12" y1="12" y2="12"/><line x1="8" x2="3" y1="12" y2="12"/><line x1="21" x2="16" y1="20" y2="20"/><line x1="12" x2="3" y1="20" y2="20"/><line x1="14" x2="14" y1="2" y2="6"/><line x1="8" x2="8" y1="10" y2="14"/><line x1="16" x2="16" y1="18" y2="22"/></svg>'
|
|
489
|
+
+ '<span>Gérer les profils…</span></a>';
|
|
490
|
+
|
|
491
|
+
if (activeProfileId && !profilesSynced) {
|
|
492
|
+
html += '<div class="menu-sep"></div>'
|
|
493
|
+
+ '<button class="menu-item" style="color:#FBBF24;" onclick="applyProfile(\'' + esc(activeProfileId) + '\')">'
|
|
494
|
+
+ '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#FBBF24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>'
|
|
495
|
+
+ '<span>Désynchronisé — resynchroniser</span></button>';
|
|
782
496
|
}
|
|
783
|
-
}
|
|
784
497
|
|
|
785
|
-
|
|
786
|
-
document.getElementById('sbModalTitle').textContent = 'Nouveau profil';
|
|
787
|
-
document.getElementById('sbModalSubmitBtn').textContent = 'Cr\u00e9er';
|
|
788
|
-
document.getElementById('sbEditId').value = '';
|
|
789
|
-
document.getElementById('sbCfgId').value = '';
|
|
790
|
-
document.getElementById('sbCfgId').readOnly = false;
|
|
791
|
-
document.getElementById('sbCfgName').value = '';
|
|
792
|
-
document.getElementById('sbCfgDesc').value = '';
|
|
793
|
-
sbRenderModalMappings({});
|
|
794
|
-
document.getElementById('sbModalOverlay').classList.add('active');
|
|
498
|
+
menu.innerHTML = html;
|
|
795
499
|
}
|
|
796
500
|
|
|
797
|
-
async function
|
|
501
|
+
async function applyProfile(id) {
|
|
502
|
+
closeAllMenus();
|
|
503
|
+
const name = document.getElementById('profileName');
|
|
504
|
+
const original = name.textContent;
|
|
505
|
+
name.textContent = 'Application…';
|
|
798
506
|
try {
|
|
799
|
-
const res = await fetch('/proxy/meta-configs');
|
|
800
|
-
const data = await res.json();
|
|
801
|
-
const cfg = data.metaConfigs[id];
|
|
802
|
-
if (!cfg) return alert('Profil introuvable');
|
|
803
|
-
|
|
804
|
-
document.getElementById('sbModalTitle').textContent = 'Modifier\u00a0: ' + cfg.name;
|
|
805
|
-
document.getElementById('sbModalSubmitBtn').textContent = 'Mettre \u00e0 jour';
|
|
806
|
-
document.getElementById('sbEditId').value = id;
|
|
807
|
-
document.getElementById('sbCfgId').value = id;
|
|
808
|
-
document.getElementById('sbCfgId').readOnly = true;
|
|
809
|
-
document.getElementById('sbCfgName').value = cfg.name;
|
|
810
|
-
document.getElementById('sbCfgDesc').value = cfg.description || '';
|
|
811
|
-
|
|
812
|
-
if (sbDiscoveredMcps.length === 0) {
|
|
813
|
-
await sidebarDiscover();
|
|
814
|
-
}
|
|
815
|
-
sbRenderModalMappings(cfg.mappings || {});
|
|
816
|
-
document.getElementById('sbModalOverlay').classList.add('active');
|
|
817
|
-
} catch (err) {
|
|
818
|
-
alert('Erreur: ' + err.message);
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
function sbRenderModalMappings(currentMappings) {
|
|
823
|
-
const container = document.getElementById('sbModalMappings');
|
|
824
|
-
const compatible = sbDiscoveredMcps.filter(m => m.configurations !== null);
|
|
825
|
-
const compatiblePaths = new Set(compatible.map(m => m.path));
|
|
826
|
-
|
|
827
|
-
sbOfflineMappings = {};
|
|
828
|
-
Object.entries(currentMappings).forEach(([path, configId]) => {
|
|
829
|
-
if (!compatiblePaths.has(path)) sbOfflineMappings[path] = configId;
|
|
830
|
-
});
|
|
831
|
-
|
|
832
|
-
if (compatible.length === 0 && Object.keys(sbOfflineMappings).length === 0) {
|
|
833
|
-
container.innerHTML = '<p style="font-size:0.75rem;color:#71717a;">Lancez d\u2019abord la d\u00e9couverte des MCPs.</p>';
|
|
834
|
-
return;
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
const onlineHtml = compatible.map(mcp => {
|
|
838
|
-
const selected = currentMappings[mcp.path] || '';
|
|
839
|
-
const options = mcp.configurations.map(c =>
|
|
840
|
-
'<option value="' + sbEsc(c.id) + '"' + (c.id === selected ? ' selected' : '') + '>'
|
|
841
|
-
+ sbEsc(c.name || c.id) + (c.active ? ' (actif)' : '') + '</option>'
|
|
842
|
-
).join('');
|
|
843
|
-
return '<div class="sb-modal-mapping-item">'
|
|
844
|
-
+ '<div style="flex:1;"><div class="mcp-label">' + sbEsc(mcp.name) + '</div>'
|
|
845
|
-
+ '<div class="mcp-path-label">' + sbEsc(mcp.path) + '</div></div>'
|
|
846
|
-
+ '<select data-mcp-path="' + sbEsc(mcp.path) + '"><option value="">-- ignorer --</option>' + options + '</select>'
|
|
847
|
-
+ '</div>';
|
|
848
|
-
}).join('');
|
|
849
|
-
|
|
850
|
-
const offlineHtml = Object.entries(sbOfflineMappings).map(([path, configId]) =>
|
|
851
|
-
'<div class="sb-modal-mapping-item" style="opacity:0.5;">'
|
|
852
|
-
+ '<div style="flex:1;"><div class="mcp-label">⚠ ' + sbEsc(path) + '</div>'
|
|
853
|
-
+ '<div class="mcp-path-label">MCP hors ligne — mapping conserv\u00e9</div></div>'
|
|
854
|
-
+ '<span style="font-size:0.8rem;color:#a1a1aa;">' + sbEsc(configId) + '</span>'
|
|
855
|
-
+ '</div>'
|
|
856
|
-
).join('');
|
|
857
|
-
|
|
858
|
-
container.innerHTML = onlineHtml + offlineHtml;
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
async function sbHandleFormSubmit(e) {
|
|
862
|
-
e.preventDefault();
|
|
863
|
-
const editId = document.getElementById('sbEditId').value;
|
|
864
|
-
const id = document.getElementById('sbCfgId').value.trim();
|
|
865
|
-
const name = document.getElementById('sbCfgName').value.trim();
|
|
866
|
-
const description = document.getElementById('sbCfgDesc').value.trim();
|
|
867
|
-
|
|
868
|
-
const mappings = { ...sbOfflineMappings };
|
|
869
|
-
document.querySelectorAll('#sbModalMappings select[data-mcp-path]').forEach(sel => {
|
|
870
|
-
if (sel.value) mappings[sel.dataset.mcpPath] = sel.value;
|
|
871
|
-
});
|
|
872
|
-
|
|
873
|
-
const btn = document.getElementById('sbModalSubmitBtn');
|
|
874
|
-
btn.disabled = true;
|
|
875
|
-
try {
|
|
876
|
-
let res;
|
|
877
|
-
if (editId) {
|
|
878
|
-
res = await fetch('/proxy/meta-configs/' + encodeURIComponent(editId), {
|
|
879
|
-
method: 'PUT',
|
|
880
|
-
headers: { 'Content-Type': 'application/json' },
|
|
881
|
-
body: JSON.stringify({ name, description, mappings })
|
|
882
|
-
});
|
|
883
|
-
} else {
|
|
884
|
-
res = await fetch('/proxy/meta-configs', {
|
|
885
|
-
method: 'POST',
|
|
886
|
-
headers: { 'Content-Type': 'application/json' },
|
|
887
|
-
body: JSON.stringify({ id, name, description, mappings })
|
|
888
|
-
});
|
|
889
|
-
}
|
|
507
|
+
const res = await fetch('/proxy/meta-configs/' + encodeURIComponent(id) + '/apply', { method: 'POST' });
|
|
890
508
|
const data = await res.json();
|
|
891
|
-
if (
|
|
892
|
-
|
|
893
|
-
|
|
509
|
+
if (data.success) {
|
|
510
|
+
activeProfileId = id;
|
|
511
|
+
profilesSynced = true;
|
|
512
|
+
renderProfileSelect();
|
|
513
|
+
renderProfileMenu();
|
|
514
|
+
startSyncPolling();
|
|
894
515
|
} else {
|
|
895
|
-
|
|
516
|
+
name.textContent = original;
|
|
517
|
+
alert('Erreur lors de l’application du profil.');
|
|
896
518
|
}
|
|
897
519
|
} catch (err) {
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
btn.disabled = false;
|
|
520
|
+
name.textContent = original;
|
|
521
|
+
alert('Erreur réseau: ' + err.message);
|
|
901
522
|
}
|
|
902
523
|
}
|
|
903
524
|
|
|
904
|
-
function
|
|
905
|
-
document.getElementById('sbModalOverlay').classList.remove('active');
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
async function sbCheckSync() {
|
|
525
|
+
async function checkSync() {
|
|
909
526
|
try {
|
|
910
527
|
const res = await fetch('/proxy/meta-configs/active/sync');
|
|
911
528
|
const data = await res.json();
|
|
912
|
-
const
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
if (!sbSynced) {
|
|
916
|
-
warning.style.display = '';
|
|
917
|
-
const activeCard = document.querySelector('#sidebarConfigs .sb-config-card.active');
|
|
918
|
-
if (activeCard) activeCard.classList.remove('active');
|
|
919
|
-
} else if (!wasSynced && sbSynced) {
|
|
920
|
-
warning.style.display = 'none';
|
|
921
|
-
await loadSidebarConfigs();
|
|
922
|
-
}
|
|
529
|
+
const was = profilesSynced;
|
|
530
|
+
profilesSynced = data.synced !== false;
|
|
531
|
+
if (was !== profilesSynced) { renderProfileSelect(); renderProfileMenu(); }
|
|
923
532
|
} catch (_) {}
|
|
924
533
|
}
|
|
925
534
|
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
renderSidebarRoutes(data.routes || []);
|
|
931
|
-
} catch (_) {}
|
|
535
|
+
function startSyncPolling() {
|
|
536
|
+
if (syncInterval || !activeProfileId) return;
|
|
537
|
+
checkSync();
|
|
538
|
+
syncInterval = setInterval(checkSync, 30000);
|
|
932
539
|
}
|
|
933
540
|
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
return;
|
|
939
|
-
}
|
|
940
|
-
el.innerHTML = routes.map(r =>
|
|
941
|
-
'<div class="sb-service-card" data-path="' + sbEsc(r.path) + '" onclick="sidebarOpenRoute(this.dataset.path)">'
|
|
942
|
-
+ '<span class="sb-service-name" title="' + sbEsc(r.name || r.path) + '">' + sbEsc(r.name || r.path) + '</span>'
|
|
943
|
-
+ '<div class="sb-service-actions">'
|
|
944
|
-
+ '<button class="sb-icon-btn sb-icon-btn-purple" data-path="' + sbEsc(r.path) + '" onclick="event.stopPropagation(); sidebarOpenRoute(this.dataset.path)" title="Ouvrir dans une nouvelle fenêtre">⛶</button>'
|
|
945
|
-
+ '<button class="sb-icon-btn sb-icon-btn-blue" data-path="' + sbEsc(r.path) + '" onclick="event.stopPropagation(); sidebarOpenRoute(this.dataset.path)" title="Ouvrir dans une nouvelle fenêtre">↗</button>'
|
|
946
|
-
+ '<button class="sb-icon-btn sb-icon-btn-amber" data-path="' + sbEsc(r.path) + '" onclick="event.stopPropagation(); sidebarCheckHealth(this.dataset.path, this)" title="Santé">❤</button>'
|
|
947
|
-
+ '</div></div>'
|
|
948
|
-
).join('');
|
|
949
|
-
}
|
|
541
|
+
window.addEventListener('beforeunload', () => { if (syncInterval) clearInterval(syncInterval); });
|
|
542
|
+
|
|
543
|
+
/* --- Services enregistrés --- */
|
|
544
|
+
let servicesLoaded = false;
|
|
950
545
|
|
|
951
|
-
function
|
|
952
|
-
|
|
546
|
+
function toggleServicesMenu(btn) {
|
|
547
|
+
toggleMenu(btn);
|
|
548
|
+
if (!servicesLoaded) loadServices();
|
|
953
549
|
}
|
|
954
550
|
|
|
955
|
-
async function
|
|
956
|
-
btn.disabled = true;
|
|
957
|
-
btn.innerHTML = '<span class="sb-spinner"></span>';
|
|
551
|
+
async function loadServices() {
|
|
958
552
|
try {
|
|
959
|
-
const res = await fetch('/proxy/
|
|
960
|
-
method: 'POST',
|
|
961
|
-
headers: { 'Content-Type': 'application/json' },
|
|
962
|
-
body: JSON.stringify({ path })
|
|
963
|
-
});
|
|
553
|
+
const res = await fetch('/proxy/routes');
|
|
964
554
|
const data = await res.json();
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
555
|
+
const routes = data.routes || [];
|
|
556
|
+
const menu = document.getElementById('servicesMenu');
|
|
557
|
+
menu.innerHTML = routes.length
|
|
558
|
+
? routes.map(r =>
|
|
559
|
+
'<a class="menu-item" href="' + esc(r.path) + '" target="_blank" rel="noopener noreferrer">'
|
|
560
|
+
+ '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg>'
|
|
561
|
+
+ '<span>' + esc(r.name || r.path) + '</span></a>').join('')
|
|
562
|
+
: '<div class="menu-empty">Aucun service enregistré</div>';
|
|
563
|
+
servicesLoaded = true;
|
|
969
564
|
} catch (_) {
|
|
970
|
-
|
|
971
|
-
btn.disabled = false;
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
async function refreshSidebarRoutes() {
|
|
976
|
-
const btn = document.getElementById('sbRefreshRoutesBtn');
|
|
977
|
-
btn.disabled = true;
|
|
978
|
-
btn.classList.add('spinning');
|
|
979
|
-
try {
|
|
980
|
-
await loadSidebarRoutes();
|
|
981
|
-
} finally {
|
|
982
|
-
btn.disabled = false;
|
|
983
|
-
btn.classList.remove('spinning');
|
|
565
|
+
document.getElementById('servicesMenu').innerHTML = '<div class="menu-empty">Erreur de chargement</div>';
|
|
984
566
|
}
|
|
985
567
|
}
|
|
986
568
|
|
|
987
|
-
|
|
988
|
-
loadSidebarRoutes();
|
|
569
|
+
loadProfiles();
|
|
989
570
|
</script>
|
|
990
571
|
</body>
|
|
991
572
|
</html>
|