@imenam/mcp-github 1.1.47 → 1.1.48

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/public/index.html +360 -219
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imenam/mcp-github",
3
- "version": "1.1.47",
3
+ "version": "1.1.48",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "files": [
package/public/index.html CHANGED
@@ -6,17 +6,37 @@
6
6
  <title>GitHub MCP</title>
7
7
  <script src="https://cdn.tailwindcss.com"></script>
8
8
  <style>
9
+ :root {
10
+ --bg: #0a0a0b;
11
+ --surface: #141416;
12
+ --surface-2: #1b1b1e;
13
+ --border: #2a2a2e;
14
+ --border-strong: #3a3a3f;
15
+ --text: #ededef;
16
+ --text-dim: #9a9aa2;
17
+ --text-muted: #6b6b73;
18
+ --accent: #4d7cfe;
19
+ --accent-hover: #6a92ff;
20
+ --danger: #e0635e;
21
+ --radius: 8px;
22
+ }
23
+
24
+ * {
25
+ -webkit-font-smoothing: antialiased;
26
+ }
27
+
9
28
  body {
10
- background-color: #0f172a;
11
- color: #f8fafc;
29
+ background-color: var(--bg);
30
+ color: var(--text);
31
+ font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
12
32
  }
13
33
 
14
34
  /* Layout */
15
35
  .app-container {
16
36
  display: grid;
17
- grid-template-columns: 320px 1fr;
18
- gap: 2rem;
19
- max-width: 1200px;
37
+ grid-template-columns: 300px 1fr;
38
+ gap: 1.5rem;
39
+ max-width: 1080px;
20
40
  margin: 0 auto;
21
41
  }
22
42
 
@@ -26,179 +46,303 @@
26
46
  }
27
47
  }
28
48
 
29
- /* iPhone style slider */
30
- .switch {
31
- position: relative;
32
- display: inline-block;
33
- width: 44px;
34
- height: 24px;
49
+ /* Surfaces */
50
+ .card {
51
+ background-color: var(--surface);
52
+ border: 1px solid var(--border);
53
+ border-radius: var(--radius);
54
+ }
55
+
56
+ .section-label {
57
+ font-size: 0.6875rem;
58
+ font-weight: 600;
59
+ letter-spacing: 0.08em;
60
+ text-transform: uppercase;
61
+ color: var(--text-muted);
62
+ }
63
+
64
+ .divider {
65
+ border-top: 1px solid var(--border);
66
+ }
67
+
68
+ /* Text helpers */
69
+ .text-main { color: var(--text); }
70
+ .text-dim { color: var(--text-dim); }
71
+ .text-muted { color: var(--text-muted); }
72
+
73
+ /* Buttons */
74
+ .btn {
75
+ display: flex;
76
+ align-items: center;
77
+ justify-content: center;
78
+ gap: 0.5rem;
79
+ width: 100%;
80
+ padding: 0.55rem 0.9rem;
81
+ font-size: 0.875rem;
82
+ font-weight: 500;
83
+ border-radius: var(--radius);
84
+ border: 1px solid transparent;
85
+ cursor: pointer;
86
+ transition: background-color .15s ease, border-color .15s ease, color .15s ease, opacity .15s ease;
87
+ }
88
+ .btn:disabled {
89
+ opacity: 0.4;
90
+ cursor: not-allowed;
91
+ }
92
+
93
+ .btn-primary {
94
+ background-color: var(--accent);
95
+ color: #fff;
96
+ }
97
+ .btn-primary:not(:disabled):hover { background-color: var(--accent-hover); }
98
+
99
+ .btn-secondary {
100
+ background-color: transparent;
101
+ border-color: var(--border);
102
+ color: var(--text-dim);
103
+ }
104
+ .btn-secondary:not(:disabled):hover {
105
+ background-color: var(--surface-2);
106
+ border-color: var(--border-strong);
107
+ color: var(--text);
108
+ }
109
+
110
+ .btn-danger {
111
+ background-color: transparent;
112
+ border-color: var(--border);
113
+ color: var(--danger);
114
+ }
115
+ .btn-danger:not(:disabled):hover {
116
+ background-color: rgba(224, 99, 94, 0.08);
117
+ border-color: var(--danger);
118
+ }
119
+
120
+ .btn-icon {
121
+ display: inline-flex;
122
+ align-items: center;
123
+ justify-content: center;
124
+ width: 2.25rem;
125
+ padding: 0;
35
126
  flex-shrink: 0;
36
127
  }
37
128
 
38
- .switch input {
39
- opacity: 0;
40
- width: 0;
41
- height: 0;
129
+ /* Form fields */
130
+ .field-label {
131
+ display: block;
132
+ font-size: 0.75rem;
133
+ font-weight: 500;
134
+ color: var(--text-dim);
135
+ margin-bottom: 0.4rem;
136
+ }
137
+
138
+ .field-input,
139
+ .field-select {
140
+ width: 100%;
141
+ padding: 0.5rem 0.75rem;
142
+ font-size: 0.875rem;
143
+ background-color: var(--bg);
144
+ border: 1px solid var(--border);
145
+ border-radius: var(--radius);
146
+ color: var(--text);
147
+ outline: none;
148
+ transition: border-color .15s ease;
149
+ }
150
+ .field-input::placeholder { color: var(--text-muted); }
151
+ .field-input:focus,
152
+ .field-select:focus { border-color: var(--accent); }
153
+ .field-select { cursor: pointer; }
154
+
155
+ /* Toggle rows */
156
+ .toggle-row {
157
+ display: flex;
158
+ align-items: center;
159
+ justify-content: space-between;
160
+ gap: 1rem;
161
+ padding: 0.65rem 0;
162
+ }
163
+ .toggle-row + .toggle-row { border-top: 1px solid var(--border); }
164
+ .toggle-row label.text {
165
+ font-size: 0.875rem;
166
+ color: var(--text-dim);
167
+ }
168
+
169
+ .info-row {
170
+ display: flex;
171
+ align-items: center;
172
+ justify-content: space-between;
173
+ gap: 1rem;
174
+ padding: 0.65rem 0.75rem;
175
+ background-color: var(--bg);
176
+ border: 1px solid var(--border);
177
+ border-radius: var(--radius);
42
178
  }
43
179
 
180
+ /* iPhone style slider (sober) */
181
+ .switch {
182
+ position: relative;
183
+ display: inline-block;
184
+ width: 40px;
185
+ height: 22px;
186
+ flex-shrink: 0;
187
+ }
188
+ .switch input { opacity: 0; width: 0; height: 0; }
44
189
  .slider {
45
190
  position: absolute;
46
191
  cursor: pointer;
47
- top: 0;
48
- left: 0;
49
- right: 0;
50
- bottom: 0;
51
- background-color: #334155;
52
- transition: .4s;
53
- border-radius: 24px;
192
+ inset: 0;
193
+ background-color: var(--border-strong);
194
+ transition: .25s;
195
+ border-radius: 22px;
54
196
  }
55
-
56
197
  .slider:before {
57
198
  position: absolute;
58
199
  content: "";
59
- height: 18px;
60
- width: 18px;
200
+ height: 16px;
201
+ width: 16px;
61
202
  left: 3px;
62
203
  bottom: 3px;
63
- background-color: white;
64
- transition: .4s;
204
+ background-color: #fff;
205
+ transition: .25s;
65
206
  border-radius: 50%;
66
207
  }
208
+ input:checked + .slider { background-color: var(--accent); }
209
+ input:checked + .slider:before { transform: translateX(18px); }
67
210
 
68
- input:checked + .slider {
69
- background-color: #2563eb;
70
- }
71
-
72
- input:checked + .slider:before {
73
- transform: translateX(20px);
74
- }
75
-
76
- /* Custom dropdown styles */
211
+ /* Custom dropdown */
77
212
  .search-results {
78
213
  position: absolute;
79
214
  top: 100%;
80
215
  left: 0;
81
216
  right: 0;
82
217
  z-index: 50;
83
- background-color: #0f172a;
84
- border: 1px solid #334155;
85
- border-radius: 0.5rem;
218
+ background-color: var(--surface-2);
219
+ border: 1px solid var(--border);
220
+ border-radius: var(--radius);
86
221
  margin-top: 0.25rem;
87
222
  max-height: 15rem;
88
223
  overflow-y: auto;
89
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
224
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
90
225
  }
91
-
92
226
  .search-result-item {
93
- padding: 0.5rem 1rem;
227
+ padding: 0.5rem 0.75rem;
94
228
  cursor: pointer;
95
- transition: background-color 0.2s;
229
+ transition: background-color .12s;
96
230
  font-size: 0.875rem;
231
+ color: var(--text-dim);
97
232
  }
98
-
99
233
  .search-result-item:hover {
100
- background-color: #1e293b;
101
- color: #60a5fa;
234
+ background-color: var(--surface);
235
+ color: var(--text);
102
236
  }
103
-
104
237
  .search-result-item.active {
105
- background-color: #1e293b;
106
- color: #60a5fa;
107
- outline: 1px solid #3b82f6;
238
+ background-color: var(--surface);
239
+ color: var(--text);
108
240
  }
109
-
110
241
  .search-result-item.selected {
111
- background-color: #2563eb;
112
- color: white;
242
+ background-color: rgba(77, 124, 254, 0.12);
243
+ color: var(--accent-hover);
244
+ }
245
+
246
+ /* Badge */
247
+ .badge {
248
+ display: inline-flex;
249
+ align-items: center;
250
+ padding: 0.15rem 0.6rem;
251
+ font-size: 0.75rem;
252
+ font-weight: 500;
253
+ color: var(--text-dim);
254
+ background-color: var(--surface-2);
255
+ border: 1px solid var(--border);
256
+ border-radius: 999px;
113
257
  }
114
258
 
259
+ /* Spinner */
260
+ .spinner {
261
+ display: inline-block;
262
+ width: 1rem;
263
+ height: 1rem;
264
+ border: 2px solid var(--border-strong);
265
+ border-top-color: var(--accent);
266
+ border-radius: 50%;
267
+ animation: spin 0.7s linear infinite;
268
+ }
269
+ @keyframes spin { to { transform: rotate(360deg); } }
270
+
115
271
  /* Toasts */
116
272
  .toast {
117
- animation: slideIn 0.3s ease-out forwards, fadeOut 0.3s ease-in forwards 4.7s;
118
- max-width: 24rem;
273
+ display: flex;
274
+ align-items: center;
275
+ gap: 0.6rem;
276
+ padding: 0.7rem 0.9rem;
277
+ font-size: 0.875rem;
278
+ background-color: var(--surface-2);
279
+ border: 1px solid var(--border);
280
+ border-left-width: 3px;
281
+ border-radius: var(--radius);
282
+ color: var(--text);
283
+ max-width: 22rem;
284
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
285
+ animation: slideIn 0.25s ease-out forwards, fadeOut 0.3s ease-in forwards 4.7s;
119
286
  }
287
+ .toast-success { border-left-color: var(--accent); }
288
+ .toast-error { border-left-color: var(--danger); }
120
289
 
121
290
  @keyframes slideIn {
122
- from { transform: translateX(100%); opacity: 0; }
291
+ from { transform: translateX(110%); opacity: 0; }
123
292
  to { transform: translateX(0); opacity: 1; }
124
293
  }
125
-
126
294
  @keyframes fadeOut {
127
295
  from { opacity: 1; }
128
296
  to { opacity: 0; }
129
297
  }
130
298
  </style>
131
299
  </head>
132
- <body class="min-h-screen p-8">
300
+ <body class="min-h-screen p-6 md:p-10">
133
301
  <div class="app-container">
134
302
  <!-- Sidebar -->
135
- <aside class="space-y-6">
136
- <div class="bg-slate-800 rounded-xl shadow-2xl p-6 border border-slate-700 sticky top-8">
137
- <h2 class="text-xl font-bold mb-4 text-blue-400 flex items-center">
138
- <svg class="w-6 h-6 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
139
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"></path>
140
- </svg>
141
- Configuration
142
- </h2>
303
+ <aside>
304
+ <div class="card p-5 sticky top-10">
305
+ <h2 class="section-label mb-4">Configuration</h2>
143
306
 
144
307
  <!-- Configuration Selector -->
145
- <div class="mb-6 space-y-2">
146
- <div class="flex gap-2">
147
- <select id="configSelector" class="flex-1 px-3 py-2 bg-slate-900 border border-slate-600 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 outline-none">
148
- <option value="">-- Sélectionner --</option>
149
- </select>
150
- <button type="button" id="newConfigBtn" class="px-3 py-2 bg-green-600 hover:bg-green-700 rounded-lg transition duration-200" title="Nouvelle configuration">
151
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
152
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
153
- </svg>
154
- </button>
155
- <button type="button" id="deleteConfigBtn" class="px-3 py-2 bg-red-600 hover:bg-red-700 disabled:bg-slate-700 disabled:cursor-not-allowed rounded-lg transition duration-200" title="Supprimer la configuration" disabled>
156
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
157
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
158
- </svg>
159
- </button>
160
- </div>
161
- </div>
162
-
163
- <div class="border-t border-slate-700 pt-4 mb-4">
164
- <h3 class="text-sm font-medium text-slate-400 mb-3">Actions</h3>
165
- </div>
166
-
167
- <div class="space-y-4">
168
- <button type="button" onclick="document.getElementById('configForm').requestSubmit()" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded-lg transition duration-200 shadow-lg flex justify-center items-center">
169
- <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
170
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
308
+ <div class="flex gap-2 mb-5">
309
+ <select id="configSelector" class="field-select flex-1">
310
+ <option value="">— Sélectionner —</option>
311
+ </select>
312
+ <button type="button" id="newConfigBtn" class="btn btn-secondary btn-icon" title="Nouvelle configuration">
313
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
314
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
171
315
  </svg>
172
- <span>Sauvegarder</span>
173
316
  </button>
174
-
175
- <button type="button" id="cloneBtn" class="w-full bg-indigo-600 hover:bg-indigo-700 disabled:bg-slate-700 disabled:cursor-not-allowed text-white font-bold py-3 px-4 rounded-lg transition duration-200 shadow-lg flex justify-center items-center">
176
- <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
177
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4"></path>
317
+ <button type="button" id="deleteConfigBtn" class="btn btn-danger btn-icon" title="Supprimer la configuration" disabled>
318
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
319
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
178
320
  </svg>
179
- <span>Cloner le dépôt</span>
180
321
  </button>
322
+ </div>
181
323
 
182
- <button type="button" id="restartBtn" class="w-full bg-amber-600 hover:bg-amber-700 text-white font-bold py-3 px-4 rounded-lg transition duration-200 shadow-lg flex justify-center items-center">
183
- <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
184
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
185
- </svg>
186
- <span>Redémarrer</span>
187
- </button>
324
+ <div class="divider pt-4">
325
+ <h3 class="section-label mb-3">Actions</h3>
188
326
 
189
- <button type="button" id="deleteWorkingDirBtn" class="w-full bg-orange-600 hover:bg-orange-700 disabled:bg-slate-700 disabled:cursor-not-allowed text-white font-bold py-3 px-4 rounded-lg transition duration-200 shadow-lg flex justify-center items-center">
190
- <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
191
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
192
- </svg>
193
- <span>Nettoyer Repo</span>
194
- </button>
327
+ <div class="space-y-2.5">
328
+ <button type="button" onclick="document.getElementById('configForm').requestSubmit()" class="btn btn-primary">
329
+ Sauvegarder
330
+ </button>
331
+
332
+ <button type="button" id="cloneBtn" class="btn btn-secondary">
333
+ Cloner le dépôt
334
+ </button>
335
+
336
+ <button type="button" id="restartBtn" class="btn btn-secondary">
337
+ Redémarrer
338
+ </button>
339
+
340
+ <button type="button" id="deleteWorkingDirBtn" class="btn btn-danger">
341
+ Nettoyer le repo
342
+ </button>
195
343
 
196
- <div class="border-t border-slate-700 pt-4">
197
- <button type="button" id="stopBtn" class="w-full bg-red-600 hover:bg-red-700 text-white font-bold py-3 px-4 rounded-lg transition duration-200 shadow-lg flex justify-center items-center">
198
- <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
199
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
200
- </svg>
201
- <span>Arrêter</span>
344
+ <button type="button" id="stopBtn" class="btn btn-danger">
345
+ Arrêter le serveur
202
346
  </button>
203
347
  </div>
204
348
  </div>
@@ -206,44 +350,44 @@
206
350
  </aside>
207
351
 
208
352
  <!-- Main Content -->
209
- <main class="bg-slate-800 rounded-xl shadow-2xl p-8 border border-slate-700">
210
- <div class="flex items-center gap-4 mb-6">
211
- <h1 class="text-3xl font-bold text-blue-400 flex items-center">
212
- <svg class="w-8 h-8 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
213
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path>
353
+ <main class="card p-7">
354
+ <div class="flex items-center gap-3 mb-7">
355
+ <h1 class="text-xl font-semibold text-main flex items-center gap-2.5">
356
+ <svg class="w-5 h-5 text-dim" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
357
+ <path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"></path>
214
358
  </svg>
215
359
  GitHub MCP
216
360
  </h1>
217
- <span id="agentNameBadge" class="hidden px-3 py-1 bg-blue-900/50 text-blue-300 text-sm font-medium rounded-full border border-blue-700"></span>
361
+ <span id="agentNameBadge" class="badge hidden"></span>
218
362
  </div>
219
-
220
- <form id="configForm" class="space-y-6">
363
+
364
+ <form id="configForm" class="space-y-5">
221
365
  <div class="space-y-4" id="inputs">
222
366
  <!-- Inputs will be injected here -->
223
367
  <div class="flex justify-center p-8">
224
- <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-400"></div>
368
+ <div class="spinner"></div>
225
369
  </div>
226
370
  </div>
227
371
  </form>
228
372
 
229
- <div class="mt-8 text-sm text-slate-400 border-t border-slate-700 pt-4 italic">
230
- Note: Le serveur MCP doit être redémarré pour appliquer les changements.
373
+ <div class="mt-8 divider pt-4 text-xs text-muted">
374
+ Le serveur MCP doit être redémarré pour appliquer les changements.
231
375
  </div>
232
376
  </main>
233
377
  </div>
234
378
 
235
379
  <!-- Toast Container -->
236
- <div id="toast-container" class="fixed bottom-8 right-8 z-50 flex flex-col gap-3"></div>
380
+ <div id="toast-container" class="fixed bottom-6 right-6 z-50 flex flex-col gap-2.5"></div>
237
381
 
238
382
  <script>
239
383
  // Détection automatique du base path pour fonctionner via proxy
240
384
  const pathname = window.location.pathname;
241
- const BASE_PATH = pathname.includes('.')
385
+ const BASE_PATH = pathname.includes('.')
242
386
  ? pathname.replace(/\/[^/]*$/, '') // /github-mcp/index.html -> /github-mcp
243
- : pathname.endsWith('/')
387
+ : pathname.endsWith('/')
244
388
  ? pathname.slice(0, -1) // /github-mcp/ -> /github-mcp
245
389
  : pathname === '/' ? '' : pathname; // /github-mcp -> /github-mcp, / -> ''
246
-
390
+
247
391
  // Helper pour construire les URLs d'API
248
392
  const api = (endpoint) => `${BASE_PATH}/${endpoint}`.replace(/\/+/g, '/');
249
393
 
@@ -299,11 +443,11 @@
299
443
  }
300
444
 
301
445
  function renderConfigSelector() {
302
- configSelector.innerHTML = '<option value="">-- Sélectionner --</option>' +
303
- configNames.map(name =>
446
+ configSelector.innerHTML = '<option value="">— Sélectionner —</option>' +
447
+ configNames.map(name =>
304
448
  `<option value="${name}" ${name === currentConfigName ? 'selected' : ''}>${name}</option>`
305
449
  ).join('');
306
-
450
+
307
451
  deleteConfigBtn.disabled = !currentConfigName || configNames.length <= 1;
308
452
  }
309
453
 
@@ -409,7 +553,7 @@
409
553
  async function fetchRepositories(searchTerm = '') {
410
554
  const loadingIndicator = document.getElementById('REPO_LOADING_INDICATOR');
411
555
  if (loadingIndicator) loadingIndicator.classList.remove('hidden');
412
-
556
+
413
557
  try {
414
558
  const url = api(`api/repositories${searchTerm ? `?search=${encodeURIComponent(searchTerm)}` : ''}`);
415
559
  const response = await fetch(url);
@@ -434,13 +578,13 @@
434
578
  resultsContainer.innerHTML = selectState.repos.map((repo, index) => `
435
579
  <div class="search-result-item ${repo === currentRepo ? 'selected' : ''}" data-index="${index}" onclick="selectRepo('${repo}')">
436
580
  ${repo}
437
- ${repo === currentRepo ? '<span class="float-right text-blue-400">✓</span>' : ''}
581
+ ${repo === currentRepo ? '<span class="float-right">✓</span>' : ''}
438
582
  </div>
439
583
  `).join('');
440
584
  resultsContainer.classList.remove('hidden');
441
585
  activeIndex = -1;
442
586
  } else {
443
- resultsContainer.innerHTML = '<div class="p-3 text-slate-500 text-sm">Aucun résultat</div>';
587
+ resultsContainer.innerHTML = '<div class="search-result-item">Aucun résultat</div>';
444
588
  resultsContainer.classList.remove('hidden');
445
589
  }
446
590
  }
@@ -450,18 +594,18 @@
450
594
  const searchInput = document.getElementById('REPO_SEARCH');
451
595
  const hiddenInput = document.getElementById('TARGET_REPO');
452
596
  const resultsContainer = document.getElementById('REPO_RESULTS');
453
-
597
+
454
598
  if (searchInput) searchInput.value = repo;
455
599
  if (hiddenInput) {
456
600
  hiddenInput.value = repo;
457
601
  currentConfig.TARGET_REPO = repo;
458
602
  }
459
603
  if (resultsContainer) resultsContainer.classList.add('hidden');
460
-
604
+
461
605
  // Réinitialiser les branches lors du changement de repository
462
606
  currentConfig.BASE_BRANCH = '';
463
607
  currentConfig.TARGET_BRANCH = '';
464
-
608
+
465
609
  // Charger les branches pour ce repo (renderInputs is called inside fetchBranches)
466
610
  await fetchBranches(repo);
467
611
  };
@@ -469,11 +613,11 @@
469
613
  // Fetch branches for a repository
470
614
  async function fetchBranches(repo, preserveConfig = false) {
471
615
  if (!repo) return [];
472
-
616
+
473
617
  selectState.loadingBranches[repo] = true;
474
618
  // Use renderInputs directly to avoid overwriting currentConfig from DOM
475
619
  renderInputs(currentConfig);
476
-
620
+
477
621
  try {
478
622
  const url = api(`api/branches?repo=${encodeURIComponent(repo)}`);
479
623
  const response = await fetch(url);
@@ -492,42 +636,42 @@
492
636
 
493
637
  // Render select field
494
638
  function renderSelectField(field, value, config) {
495
- let options = '<option value="">-- Sélectionner une option --</option>';
496
-
639
+ let options = '<option value="">— Sélectionner une option —</option>';
640
+
497
641
  if (field.id === 'TARGET_REPO') {
498
642
  if (selectState.loadingRepos && selectState.repos.length === 0) {
499
643
  return `
500
- <div class="space-y-1">
501
- <label for="${field.id}" class="block text-sm font-medium text-slate-300">${field.label}</label>
502
- <div id="${field.id}_loading" class="w-full px-4 py-2 bg-slate-900 border border-slate-600 rounded-lg text-slate-400 flex items-center">
503
- <div class="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-400 mr-2"></div>
644
+ <div>
645
+ <label for="${field.id}" class="field-label">${field.label}</label>
646
+ <div id="${field.id}_loading" class="field-input flex items-center gap-2 text-dim">
647
+ <div class="spinner"></div>
504
648
  Chargement des dépôts...
505
649
  </div>
506
650
  </div>
507
651
  `;
508
652
  }
509
-
510
- options += selectState.repos.map(repo =>
653
+
654
+ options += selectState.repos.map(repo =>
511
655
  `<option value="${repo}" ${value === repo ? 'selected' : ''}>${repo}</option>`
512
656
  ).join('');
513
-
657
+
514
658
  if (value && !selectState.repos.includes(value)) {
515
659
  options += `<option value="${value}" selected>${value}</option>`;
516
660
  }
517
661
 
518
662
  return `
519
- <div class="space-y-1">
520
- <label for="${field.id}" class="block text-sm font-medium text-slate-300">${field.label}</label>
663
+ <div>
664
+ <label for="${field.id}" class="field-label">${field.label}</label>
521
665
  <div class="relative">
522
666
  <div class="relative">
523
667
  <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
524
- <svg class="h-4 w-4 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
668
+ <svg class="h-4 w-4 text-muted" fill="none" stroke="currentColor" viewBox="0 0 24 24">
525
669
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
526
670
  </svg>
527
671
  </div>
528
- <input type="text" id="REPO_SEARCH" placeholder="Rechercher un dépôt..." class="w-full pl-10 pr-4 py-2 bg-slate-900 border border-slate-700 rounded-lg focus:ring-2 focus:ring-blue-500 outline-none text-sm text-slate-200" autocomplete="off" value="${value || ''}">
672
+ <input type="text" id="REPO_SEARCH" placeholder="Rechercher un dépôt..." class="field-input" style="padding-left:2.25rem" autocomplete="off" value="${value || ''}">
529
673
  <div id="REPO_LOADING_INDICATOR" class="absolute inset-y-0 right-0 pr-3 flex items-center hidden">
530
- <div class="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-400"></div>
674
+ <div class="spinner"></div>
531
675
  </div>
532
676
  </div>
533
677
  <div id="REPO_RESULTS" class="search-results hidden"></div>
@@ -538,29 +682,29 @@
538
682
  } else if (field.id === 'BASE_BRANCH') {
539
683
  const currentRepo = config.TARGET_REPO;
540
684
  const branches = selectState.branches[currentRepo] || [];
541
-
685
+
542
686
  if (selectState.loadingBranches[currentRepo]) {
543
687
  return `
544
- <div class="space-y-1">
545
- <label for="${field.id}" class="block text-sm font-medium text-slate-300">${field.label}</label>
546
- <div id="${field.id}_loading" class="w-full px-4 py-2 bg-slate-900 border border-slate-600 rounded-lg text-slate-400 flex items-center">
547
- <div class="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-400 mr-2"></div>
688
+ <div>
689
+ <label for="${field.id}" class="field-label">${field.label}</label>
690
+ <div id="${field.id}_loading" class="field-input flex items-center gap-2 text-dim">
691
+ <div class="spinner"></div>
548
692
  Chargement des branches...
549
693
  </div>
550
694
  </div>
551
695
  `;
552
696
  }
553
-
697
+
554
698
  // Recréer les options avec l'option par défaut sélectionnée si value est vide
555
- let branchOptions = `<option value="" ${!value ? 'selected' : ''}>-- Choisir une branche --</option>`;
699
+ let branchOptions = `<option value="" ${!value ? 'selected' : ''}>— Choisir une branche —</option>`;
556
700
  branchOptions += branches.map(branch =>
557
701
  `<option value="${branch}" ${value === branch ? 'selected' : ''}>${branch}</option>`
558
702
  ).join('');
559
-
703
+
560
704
  return `
561
- <div class="space-y-1">
562
- <label for="${field.id}" class="block text-sm font-medium text-slate-300">${field.label}</label>
563
- <select id="${field.id}" class="w-full px-4 py-2 bg-slate-900 border border-slate-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition duration-200 text-slate-100">
705
+ <div>
706
+ <label for="${field.id}" class="field-label">${field.label}</label>
707
+ <select id="${field.id}" class="field-select">
564
708
  ${branchOptions}
565
709
  </select>
566
710
  </div>
@@ -568,44 +712,44 @@
568
712
  } else if (field.id === 'TARGET_BRANCH') {
569
713
  const currentRepo = config.TARGET_REPO;
570
714
  const branches = selectState.branches[currentRepo] || [];
571
-
715
+
572
716
  if (selectState.loadingBranches[currentRepo]) {
573
717
  return `
574
- <div class="space-y-1">
575
- <label for="${field.id}" class="block text-sm font-medium text-slate-300">${field.label}</label>
576
- <div id="${field.id}_loading" class="w-full px-4 py-2 bg-slate-900 border border-slate-600 rounded-lg text-slate-400 flex items-center">
577
- <div class="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-400 mr-2"></div>
718
+ <div>
719
+ <label for="${field.id}" class="field-label">${field.label}</label>
720
+ <div id="${field.id}_loading" class="field-input flex items-center gap-2 text-dim">
721
+ <div class="spinner"></div>
578
722
  Chargement des branches...
579
723
  </div>
580
724
  </div>
581
725
  `;
582
726
  }
583
-
584
- let selectOptions = '<option value="">-- Créer une nouvelle branche --</option>';
727
+
728
+ let selectOptions = '<option value="">— Créer une nouvelle branche —</option>';
585
729
  selectOptions += branches.map(branch =>
586
730
  `<option value="${branch}" ${value === branch ? 'selected' : ''}>${branch}</option>`
587
731
  ).join('');
588
-
732
+
589
733
  const isCustom = value && !branches.includes(value);
590
734
  const shouldShowCustom = isCustom || value === '';
591
-
735
+
592
736
  return `
593
- <div class="space-y-1">
594
- <label for="${field.id}" class="block text-sm font-medium text-slate-300">${field.label}</label>
737
+ <div>
738
+ <label for="${field.id}" class="field-label">${field.label}</label>
595
739
  <div class="space-y-2">
596
- <select id="${field.id}" class="w-full px-4 py-2 bg-slate-900 border border-slate-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition duration-200 text-slate-100">
740
+ <select id="${field.id}" class="field-select">
597
741
  ${selectOptions}
598
742
  </select>
599
- <input type="text" id="${field.id}_custom" placeholder="Entrez le nom de la nouvelle branche" value="${isCustom ? value : ''}" class="w-full px-4 py-2 bg-slate-900 border border-slate-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition duration-200 text-slate-100 ${shouldShowCustom ? '' : 'hidden'}">
743
+ <input type="text" id="${field.id}_custom" placeholder="Entrez le nom de la nouvelle branche" value="${isCustom ? value : ''}" class="field-input ${shouldShowCustom ? '' : 'hidden'}">
600
744
  </div>
601
745
  </div>
602
746
  `;
603
747
  }
604
-
748
+
605
749
  return `
606
- <div class="space-y-1">
607
- <label for="${field.id}" class="block text-sm font-medium text-slate-300">${field.label}</label>
608
- <select id="${field.id}" class="w-full px-4 py-2 bg-slate-900 border border-slate-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition duration-200 text-slate-100">
750
+ <div>
751
+ <label for="${field.id}" class="field-label">${field.label}</label>
752
+ <select id="${field.id}" class="field-select">
609
753
  ${options}
610
754
  </select>
611
755
  </div>
@@ -624,7 +768,7 @@
624
768
  throw new Error(data.error || 'Erreur lors du chargement');
625
769
  }
626
770
  currentConfig = await response.json();
627
-
771
+
628
772
  // Afficher le nom de l'agent si défini
629
773
  const agentBadge = document.getElementById('agentNameBadge');
630
774
  if (currentConfig.AGENT_NAME) {
@@ -633,13 +777,13 @@
633
777
  } else {
634
778
  agentBadge.classList.add('hidden');
635
779
  }
636
-
780
+
637
781
  // Le token est désormais côté serveur, on tente de charger les repos directement
638
782
  await fetchRepositories();
639
783
  if (currentConfig.TARGET_REPO) {
640
784
  await fetchBranches(currentConfig.TARGET_REPO);
641
785
  }
642
-
786
+
643
787
  renderInputs(currentConfig);
644
788
  } catch (error) {
645
789
  showStatus(error.message || 'Erreur lors du chargement de la configuration', 'error');
@@ -650,17 +794,17 @@
650
794
  function renderInputs(config) {
651
795
  // CLONE_DIR en lecture seule (variable d'environnement)
652
796
  const cloneDirInfo = config.CLONE_DIR
653
- ? `<div class="p-3 bg-slate-700/50 rounded-lg flex items-center justify-between">
654
- <span class="text-sm font-medium text-slate-400">Répertoire de clonage</span>
655
- <span class="text-sm text-slate-200 font-mono">${config.CLONE_DIR}</span>
797
+ ? `<div class="info-row">
798
+ <span class="text-xs font-medium text-dim">Répertoire de clonage</span>
799
+ <span class="text-xs text-main font-mono">${config.CLONE_DIR}</span>
656
800
  </div>`
657
801
  : '';
658
802
 
659
803
  inputsContainer.innerHTML = fields.map(field => {
660
804
  if (field.type === 'slider') {
661
805
  return `
662
- <div class="flex items-center justify-between p-3 bg-slate-700/50 rounded-lg">
663
- <label for="${field.id}" class="text-sm font-medium text-slate-200">${field.label}</label>
806
+ <div class="toggle-row">
807
+ <label for="${field.id}" class="text">${field.label}</label>
664
808
  <label class="switch">
665
809
  <input type="checkbox" id="${field.id}" ${config[field.id] === 'true' || config[field.id] === true ? 'checked' : ''}>
666
810
  <span class="slider"></span>
@@ -670,9 +814,9 @@
670
814
  }
671
815
  if (field.type === 'checkbox') {
672
816
  return `
673
- <div class="flex items-center space-x-3 p-3 bg-slate-700/50 rounded-lg">
674
- <input type="checkbox" id="${field.id}" ${config[field.id] === 'true' || config[field.id] === true ? 'checked' : ''} class="w-5 h-5 text-blue-600 rounded focus:ring-blue-500 bg-slate-900 border-slate-600">
675
- <label for="${field.id}" class="text-sm font-medium text-slate-200">${field.label}</label>
817
+ <div class="toggle-row">
818
+ <label for="${field.id}" class="text">${field.label}</label>
819
+ <input type="checkbox" id="${field.id}" ${config[field.id] === 'true' || config[field.id] === true ? 'checked' : ''} class="w-4 h-4">
676
820
  </div>
677
821
  `;
678
822
  }
@@ -680,13 +824,13 @@
680
824
  return renderSelectField(field, config[field.id] || '', config);
681
825
  }
682
826
  return `
683
- <div class="space-y-1">
684
- <label for="${field.id}" class="block text-sm font-medium text-slate-300">${field.label}</label>
685
- <input type="${field.type}" id="${field.id}" value="${config[field.id] || ''}" placeholder="${field.placeholder}" class="w-full px-4 py-2 bg-slate-900 border border-slate-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition duration-200 text-slate-100">
827
+ <div>
828
+ <label for="${field.id}" class="field-label">${field.label}</label>
829
+ <input type="${field.type}" id="${field.id}" value="${config[field.id] || ''}" placeholder="${field.placeholder}" class="field-input">
686
830
  </div>
687
831
  `;
688
832
  }).join('') + cloneDirInfo;
689
-
833
+
690
834
  if (deleteWorkingDirBtn) {
691
835
  deleteWorkingDirBtn.disabled = !config.REPO_PATH;
692
836
  }
@@ -694,7 +838,7 @@
694
838
  if (cloneBtn) {
695
839
  cloneBtn.disabled = !config.TARGET_REPO || !config.TARGET_BRANCH;
696
840
  }
697
-
841
+
698
842
  setupEventListeners();
699
843
  }
700
844
 
@@ -722,7 +866,7 @@
722
866
  const targetBranchSelect = document.getElementById('TARGET_BRANCH');
723
867
  const repoSearch = document.getElementById('REPO_SEARCH');
724
868
  const resultsContainer = document.getElementById('REPO_RESULTS');
725
-
869
+
726
870
  if (repoSearch) {
727
871
  // Focus: ouvre la liste avec ce qui est déjà là
728
872
  repoSearch.addEventListener('focus', () => {
@@ -793,7 +937,7 @@
793
937
  else config[f.id] = inp.value;
794
938
  }
795
939
  });
796
-
940
+
797
941
  try {
798
942
  await fetch(api('api/config'), {
799
943
  method: 'POST',
@@ -807,12 +951,12 @@
807
951
  });
808
952
  }
809
953
  });
810
-
954
+
811
955
  if (targetBranchSelect) {
812
956
  targetBranchSelect.addEventListener('change', () => {
813
957
  const customInput = document.getElementById('TARGET_BRANCH_custom');
814
958
  if (!customInput) return;
815
-
959
+
816
960
  const isNewBranch = targetBranchSelect.value === '';
817
961
  customInput.classList.toggle('hidden', !isNewBranch);
818
962
  if (isNewBranch) {
@@ -827,19 +971,16 @@
827
971
  function showStatus(message, type) {
828
972
  const container = document.getElementById('toast-container');
829
973
  const toast = document.createElement('div');
830
-
831
- const bgColor = type === 'success' ? 'bg-green-900/90' : 'bg-red-900/90';
832
- const borderColor = type === 'success' ? 'border-green-500' : 'border-red-500';
833
- const textColor = type === 'success' ? 'text-green-100' : 'text-red-100';
834
- const icon = type === 'success'
835
- ? '<svg class="w-5 h-5 mr-3 text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>'
836
- : '<svg class="w-5 h-5 mr-3 text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>';
837
-
838
- toast.className = `toast flex items-center p-4 rounded-lg border shadow-xl ${bgColor} ${borderColor} ${textColor}`;
974
+
975
+ const icon = type === 'success'
976
+ ? '<svg class="w-4 h-4 shrink-0" style="color:var(--accent)" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>'
977
+ : '<svg class="w-4 h-4 shrink-0" style="color:var(--danger)" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>';
978
+
979
+ toast.className = `toast ${type === 'success' ? 'toast-success' : 'toast-error'}`;
839
980
  toast.innerHTML = `${icon}<span class="flex-1">${message}</span>`;
840
-
981
+
841
982
  container.appendChild(toast);
842
-
983
+
843
984
  // Supprimer le toast après l'animation (5s total)
844
985
  setTimeout(() => {
845
986
  toast.remove();
@@ -898,7 +1039,7 @@
898
1039
  try {
899
1040
  restartBtn.disabled = true;
900
1041
  restartBtn.innerHTML = '<span>Redémarrage en cours...</span>';
901
-
1042
+
902
1043
  const response = await fetch(api('api/restart'), { method: 'POST' });
903
1044
 
904
1045
  if (response.ok) {
@@ -923,7 +1064,7 @@
923
1064
  try {
924
1065
  stopBtn.disabled = true;
925
1066
  stopBtn.innerHTML = '<span>Arrêt en cours...</span>';
926
-
1067
+
927
1068
  const response = await fetch(api('api/stop'), { method: 'POST' });
928
1069
 
929
1070
  if (response.ok) {
@@ -947,7 +1088,7 @@
947
1088
  try {
948
1089
  deleteWorkingDirBtn.disabled = true;
949
1090
  deleteWorkingDirBtn.innerHTML = '<span>Suppression...</span>';
950
-
1091
+
951
1092
  const response = await fetch(api('api/delete-working-dir'), { method: 'POST' });
952
1093
 
953
1094
  if (response.ok) {
@@ -975,7 +1116,7 @@
975
1116
  try {
976
1117
  cloneBtn.disabled = true;
977
1118
  cloneBtn.innerHTML = '<span>Clonage...</span>';
978
-
1119
+
979
1120
  const response = await fetch(api('api/clone'), { method: 'POST' });
980
1121
 
981
1122
  if (response.ok) {