@jtalk22/slack-mcp 1.0.6 → 1.1.0

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/public/demo.html CHANGED
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Slack MCP Server - Demo</title>
6
+ <title>Slack MCP Server - Interactive Demo</title>
7
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
9
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
@@ -17,6 +17,8 @@
17
17
  --accent-hover: #45b7aa;
18
18
  --accent-glow: rgba(78, 205, 196, 0.3);
19
19
  --danger: #e94560;
20
+ --claude-orange: #da7756;
21
+ --claude-bg: #2d2a24;
20
22
  --text-primary: #ffffff;
21
23
  --text-secondary: rgba(255, 255, 255, 0.7);
22
24
  --text-muted: rgba(255, 255, 255, 0.5);
@@ -34,887 +36,989 @@
34
36
  line-height: 1.5;
35
37
  }
36
38
 
37
- /* Gradient background */
38
- .bg-gradient {
39
- position: fixed;
39
+ /* Preview Banner */
40
+ .preview-banner {
41
+ background: linear-gradient(135deg, var(--danger), #ff8c00);
42
+ color: white;
43
+ padding: 12px 20px;
44
+ text-align: center;
45
+ font-size: 14px;
46
+ font-weight: 500;
47
+ position: sticky;
40
48
  top: 0;
41
- left: 0;
42
- right: 0;
43
- bottom: 0;
44
- background:
45
- radial-gradient(ellipse at 20% 20%, rgba(78, 205, 196, 0.08) 0%, transparent 50%),
46
- radial-gradient(ellipse at 80% 80%, rgba(233, 69, 96, 0.05) 0%, transparent 50%),
47
- var(--bg-primary);
48
- z-index: -1;
49
- }
50
-
51
- .container {
52
- max-width: 1400px;
53
- margin: 0 auto;
54
- padding: 24px;
49
+ z-index: 100;
50
+ }
51
+ .preview-banner code {
52
+ background: rgba(0,0,0,0.2);
53
+ padding: 2px 8px;
54
+ border-radius: 4px;
55
+ font-family: monospace;
56
+ }
57
+ .preview-banner a {
58
+ color: white;
59
+ text-decoration: underline;
60
+ }
61
+
62
+ /* Main Layout */
63
+ .split-container {
64
+ display: grid;
65
+ grid-template-columns: 420px 1fr;
66
+ height: calc(100vh - 44px);
67
+ overflow: hidden;
55
68
  }
56
69
 
57
- /* Header */
58
- .header {
70
+ /* ========== LEFT PANEL: CLAUDE CHAT ========== */
71
+ .claude-panel {
72
+ background: var(--claude-bg);
73
+ border-right: 1px solid rgba(255,255,255,0.1);
59
74
  display: flex;
60
- align-items: center;
61
- justify-content: space-between;
62
- margin-bottom: 24px;
63
- padding-bottom: 24px;
64
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
75
+ flex-direction: column;
76
+ overflow: hidden;
65
77
  }
66
78
 
67
- .logo {
79
+ .claude-header {
80
+ padding: 16px 20px;
81
+ border-bottom: 1px solid rgba(255,255,255,0.1);
68
82
  display: flex;
69
83
  align-items: center;
70
84
  gap: 12px;
71
85
  }
72
86
 
73
- .logo-icon {
74
- width: 48px;
75
- height: 48px;
76
- background: linear-gradient(135deg, var(--accent), #7c3aed);
77
- border-radius: 12px;
87
+ .claude-logo {
88
+ width: 36px;
89
+ height: 36px;
90
+ background: linear-gradient(135deg, var(--claude-orange), #c4694a);
91
+ border-radius: 10px;
78
92
  display: flex;
79
93
  align-items: center;
80
94
  justify-content: center;
81
- font-size: 24px;
82
- box-shadow: 0 4px 20px var(--accent-glow);
83
- }
84
-
85
- .logo h1 {
86
- font-size: 24px;
87
95
  font-weight: 700;
88
- background: linear-gradient(135deg, var(--accent), #fff);
89
- -webkit-background-clip: text;
90
- -webkit-text-fill-color: transparent;
91
- background-clip: text;
96
+ font-size: 18px;
97
+ color: white;
92
98
  }
93
99
 
94
- .logo .subtitle {
95
- font-size: 13px;
100
+ .claude-header h2 {
101
+ font-size: 16px;
102
+ font-weight: 600;
103
+ color: var(--text-primary);
104
+ }
105
+ .claude-header .subtitle {
106
+ font-size: 12px;
96
107
  color: var(--text-muted);
97
- font-weight: 400;
98
108
  }
99
109
 
100
- .demo-badge {
101
- background: linear-gradient(135deg, var(--danger), #ff8c00);
102
- color: white;
103
- padding: 6px 16px;
104
- border-radius: 20px;
110
+ .reset-btn {
111
+ margin-left: auto;
112
+ padding: 6px 14px;
113
+ background: rgba(255,255,255,0.1);
114
+ border: 1px solid rgba(255,255,255,0.2);
115
+ border-radius: 6px;
116
+ color: var(--text-secondary);
117
+ cursor: pointer;
105
118
  font-size: 12px;
106
- font-weight: 600;
107
- text-transform: uppercase;
108
- letter-spacing: 0.5px;
109
- animation: pulse 2s infinite;
119
+ font-family: inherit;
120
+ transition: var(--transition);
121
+ }
122
+ .reset-btn:hover {
123
+ background: rgba(233, 69, 96, 0.2);
124
+ border-color: var(--danger);
125
+ color: var(--danger);
110
126
  }
111
127
 
112
- @keyframes pulse {
113
- 0%, 100% { opacity: 1; }
114
- 50% { opacity: 0.7; }
128
+ .claude-chat {
129
+ flex: 1;
130
+ overflow-y: auto;
131
+ padding: 20px;
132
+ scroll-behavior: smooth;
115
133
  }
116
134
 
117
- /* Status bar */
118
- .status-bar {
119
- background: var(--bg-tertiary);
120
- padding: 16px 20px;
121
- border-radius: var(--border-radius);
122
- margin-bottom: 24px;
123
- display: flex;
124
- align-items: center;
125
- justify-content: space-between;
126
- border: 1px solid rgba(255, 255, 255, 0.05);
135
+ .chat-message {
136
+ margin-bottom: 20px;
137
+ animation: fadeIn 0.3s ease;
127
138
  }
128
139
 
129
- .status-indicator {
130
- display: flex;
131
- align-items: center;
132
- gap: 10px;
140
+ @keyframes fadeIn {
141
+ from { opacity: 0; transform: translateY(10px); }
142
+ to { opacity: 1; transform: translateY(0); }
133
143
  }
134
144
 
135
- .status-dot {
136
- width: 10px;
137
- height: 10px;
138
- background: #22c55e;
139
- border-radius: 50%;
140
- box-shadow: 0 0 10px rgba(34, 197, 94, 0.5);
141
- animation: blink 2s infinite;
145
+ .chat-message.user {
146
+ text-align: right;
142
147
  }
143
148
 
144
- @keyframes blink {
145
- 0%, 100% { opacity: 1; }
146
- 50% { opacity: 0.5; }
149
+ .chat-message.user .bubble {
150
+ background: var(--bg-input);
151
+ display: inline-block;
152
+ text-align: left;
147
153
  }
148
154
 
149
- .status-text {
155
+ .chat-message .bubble {
156
+ background: rgba(255,255,255,0.05);
157
+ padding: 14px 18px;
158
+ border-radius: 16px;
159
+ max-width: 90%;
150
160
  font-size: 14px;
151
- color: var(--text-secondary);
161
+ line-height: 1.6;
162
+ }
163
+
164
+ .chat-message.claude .bubble {
165
+ border-top-left-radius: 4px;
166
+ }
167
+
168
+ .chat-message.user .bubble {
169
+ border-top-right-radius: 4px;
170
+ }
171
+
172
+ /* Tool call styling */
173
+ .tool-call {
174
+ background: rgba(78, 205, 196, 0.1);
175
+ border: 1px solid rgba(78, 205, 196, 0.3);
176
+ border-radius: 8px;
177
+ padding: 12px;
178
+ margin: 10px 0;
179
+ font-family: 'SF Mono', 'Fira Code', monospace;
180
+ font-size: 12px;
152
181
  }
153
182
 
154
- .status-text strong {
183
+ .tool-call .tool-name {
155
184
  color: var(--accent);
185
+ font-weight: 600;
156
186
  }
157
187
 
158
- .workspace-info {
159
- display: flex;
160
- align-items: center;
161
- gap: 16px;
162
- font-size: 13px;
188
+ .tool-call .tool-params {
163
189
  color: var(--text-muted);
190
+ margin-top: 4px;
164
191
  }
165
192
 
166
- .workspace-info span {
193
+ /* Typing indicator */
194
+ .typing-indicator {
167
195
  display: flex;
168
- align-items: center;
169
- gap: 6px;
196
+ gap: 4px;
197
+ padding: 8px 0;
170
198
  }
171
199
 
172
- /* Main grid */
173
- .grid {
174
- display: grid;
175
- grid-template-columns: 320px 1fr;
176
- gap: 24px;
177
- height: calc(100vh - 220px);
178
- min-height: 500px;
200
+ .typing-indicator span {
201
+ width: 8px;
202
+ height: 8px;
203
+ background: var(--claude-orange);
204
+ border-radius: 50%;
205
+ animation: bounce 1.4s infinite ease-in-out;
179
206
  }
180
207
 
181
- /* Sidebar */
182
- .sidebar {
183
- background: var(--bg-secondary);
184
- border-radius: var(--border-radius);
185
- border: 1px solid rgba(255, 255, 255, 0.05);
186
- display: flex;
187
- flex-direction: column;
188
- overflow: hidden;
208
+ .typing-indicator span:nth-child(1) { animation-delay: 0s; }
209
+ .typing-indicator span:nth-child(2) { animation-delay: 0.2s; }
210
+ .typing-indicator span:nth-child(3) { animation-delay: 0.4s; }
211
+
212
+ @keyframes bounce {
213
+ 0%, 80%, 100% { transform: scale(0.6); opacity: 0.4; }
214
+ 40% { transform: scale(1); opacity: 1; }
189
215
  }
190
216
 
191
- .sidebar-header {
192
- padding: 20px;
193
- border-bottom: 1px solid rgba(255, 255, 255, 0.05);
217
+ /* Try It Buttons */
218
+ .try-buttons {
219
+ padding: 16px 20px;
220
+ border-top: 1px solid rgba(255,255,255,0.1);
221
+ background: rgba(0,0,0,0.2);
194
222
  }
195
223
 
196
- .sidebar-header h3 {
224
+ .try-buttons h4 {
197
225
  font-size: 11px;
198
- font-weight: 600;
199
226
  text-transform: uppercase;
200
227
  letter-spacing: 1px;
201
228
  color: var(--text-muted);
202
229
  margin-bottom: 12px;
203
230
  }
204
231
 
205
- .tabs {
232
+ .try-buttons .scenarios {
206
233
  display: flex;
234
+ flex-direction: column;
207
235
  gap: 8px;
208
236
  }
209
237
 
210
- .tabs button {
211
- flex: 1;
212
- padding: 10px 16px;
213
- background: var(--bg-input);
214
- color: var(--text-secondary);
215
- border: none;
216
- border-radius: 8px;
238
+ .scenario-btn {
239
+ display: flex;
240
+ align-items: center;
241
+ gap: 12px;
242
+ padding: 12px 16px;
243
+ background: rgba(255,255,255,0.05);
244
+ border: 1px solid rgba(255,255,255,0.1);
245
+ border-radius: 10px;
246
+ color: var(--text-primary);
217
247
  cursor: pointer;
218
- font-size: 13px;
219
- font-weight: 500;
220
248
  transition: var(--transition);
221
- font-family: inherit;
249
+ text-align: left;
222
250
  }
223
251
 
224
- .tabs button:hover {
252
+ .scenario-btn:hover {
225
253
  background: rgba(78, 205, 196, 0.1);
226
- color: var(--accent);
254
+ border-color: var(--accent);
227
255
  }
228
256
 
229
- .tabs button.active {
230
- background: var(--accent);
231
- color: var(--bg-primary);
257
+ .scenario-btn:disabled {
258
+ opacity: 0.5;
259
+ cursor: not-allowed;
232
260
  }
233
261
 
234
- .conversation-list {
235
- list-style: none;
262
+ .scenario-btn .icon {
263
+ font-size: 20px;
264
+ }
265
+
266
+ .scenario-btn .text {
236
267
  flex: 1;
237
- overflow-y: auto;
238
- padding: 12px;
239
268
  }
240
269
 
241
- .conversation-list::-webkit-scrollbar {
242
- width: 6px;
270
+ .scenario-btn .title {
271
+ font-weight: 600;
272
+ font-size: 13px;
273
+ }
274
+
275
+ .scenario-btn .desc {
276
+ font-size: 11px;
277
+ color: var(--text-muted);
278
+ }
279
+
280
+ /* ========== RIGHT PANEL: SLACK DASHBOARD ========== */
281
+ .dashboard-panel {
282
+ background: var(--bg-primary);
283
+ display: flex;
284
+ flex-direction: column;
285
+ overflow: hidden;
286
+ }
287
+
288
+ .dashboard-header {
289
+ padding: 16px 24px;
290
+ border-bottom: 1px solid rgba(255,255,255,0.1);
291
+ display: flex;
292
+ align-items: center;
293
+ justify-content: space-between;
294
+ background: var(--bg-secondary);
295
+ }
296
+
297
+ .dashboard-title {
298
+ display: flex;
299
+ align-items: center;
300
+ gap: 12px;
301
+ }
302
+
303
+ .dashboard-title h2 {
304
+ font-size: 18px;
305
+ font-weight: 600;
306
+ }
307
+
308
+ .status-badge {
309
+ background: rgba(34, 197, 94, 0.2);
310
+ color: #22c55e;
311
+ padding: 4px 12px;
312
+ border-radius: 20px;
313
+ font-size: 12px;
314
+ font-weight: 500;
315
+ }
316
+
317
+ .dashboard-content {
318
+ flex: 1;
319
+ display: grid;
320
+ grid-template-columns: 280px 1fr;
321
+ overflow: hidden;
322
+ }
323
+
324
+ /* Sidebar */
325
+ .sidebar {
326
+ background: var(--bg-secondary);
327
+ border-right: 1px solid rgba(255,255,255,0.05);
328
+ display: flex;
329
+ flex-direction: column;
330
+ overflow: hidden;
331
+ }
332
+
333
+ .sidebar-section {
334
+ padding: 16px;
335
+ border-bottom: 1px solid rgba(255,255,255,0.05);
243
336
  }
244
337
 
245
- .conversation-list::-webkit-scrollbar-track {
246
- background: transparent;
338
+ .sidebar-section h3 {
339
+ font-size: 11px;
340
+ text-transform: uppercase;
341
+ letter-spacing: 1px;
342
+ color: var(--text-muted);
343
+ margin-bottom: 12px;
247
344
  }
248
345
 
249
- .conversation-list::-webkit-scrollbar-thumb {
250
- background: rgba(255, 255, 255, 0.1);
251
- border-radius: 3px;
346
+ .conversation-list {
347
+ list-style: none;
348
+ flex: 1;
349
+ overflow-y: auto;
350
+ padding: 8px;
252
351
  }
253
352
 
254
353
  .conversation-item {
255
- padding: 14px 16px;
256
- border-radius: 10px;
354
+ padding: 12px 14px;
355
+ border-radius: 8px;
257
356
  cursor: pointer;
258
- margin-bottom: 6px;
259
- background: transparent;
260
- transition: var(--transition);
357
+ margin-bottom: 4px;
261
358
  display: flex;
262
359
  align-items: center;
263
360
  gap: 12px;
361
+ transition: var(--transition);
264
362
  }
265
363
 
266
364
  .conversation-item:hover {
267
- background: rgba(255, 255, 255, 0.05);
365
+ background: rgba(255,255,255,0.05);
268
366
  }
269
367
 
270
368
  .conversation-item.active {
271
369
  background: linear-gradient(135deg, var(--accent), #3b82f6);
272
- box-shadow: 0 4px 15px var(--accent-glow);
273
370
  }
274
371
 
275
372
  .conversation-item.active .conv-name,
276
- .conversation-item.active .conv-type {
373
+ .conversation-item.active .conv-preview {
277
374
  color: var(--bg-primary);
278
375
  }
279
376
 
377
+ .conversation-item.highlight {
378
+ background: rgba(78, 205, 196, 0.2);
379
+ border: 1px solid var(--accent);
380
+ animation: pulse-highlight 1s ease-in-out;
381
+ }
382
+
383
+ @keyframes pulse-highlight {
384
+ 0%, 100% { box-shadow: 0 0 0 0 var(--accent-glow); }
385
+ 50% { box-shadow: 0 0 20px 5px var(--accent-glow); }
386
+ }
387
+
280
388
  .avatar {
281
- width: 40px;
282
- height: 40px;
283
- border-radius: 10px;
389
+ width: 36px;
390
+ height: 36px;
391
+ border-radius: 8px;
284
392
  display: flex;
285
393
  align-items: center;
286
394
  justify-content: center;
287
395
  font-weight: 600;
288
- font-size: 14px;
396
+ font-size: 13px;
289
397
  flex-shrink: 0;
290
398
  }
291
399
 
292
400
  .avatar.dm { background: linear-gradient(135deg, #8b5cf6, #ec4899); }
293
401
  .avatar.channel { background: linear-gradient(135deg, #3b82f6, #06b6d4); }
294
- .avatar.group { background: linear-gradient(135deg, #f59e0b, #ef4444); }
295
-
296
- .conv-info {
297
- flex: 1;
298
- min-width: 0;
299
- }
300
-
301
- .conv-name {
302
- font-weight: 500;
303
- font-size: 14px;
304
- color: var(--text-primary);
305
- white-space: nowrap;
306
- overflow: hidden;
307
- text-overflow: ellipsis;
308
- }
309
402
 
310
- .conv-type {
311
- font-size: 12px;
312
- color: var(--text-muted);
313
- margin-top: 2px;
314
- }
315
-
316
- .unread-badge {
317
- background: var(--danger);
318
- color: white;
319
- font-size: 11px;
320
- font-weight: 600;
321
- padding: 2px 8px;
322
- border-radius: 10px;
323
- min-width: 20px;
324
- text-align: center;
325
- }
403
+ .conv-info { flex: 1; min-width: 0; }
404
+ .conv-name { font-weight: 500; font-size: 14px; }
405
+ .conv-preview { font-size: 12px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
326
406
 
327
- /* Main panel */
328
- .main-panel {
329
- background: var(--bg-secondary);
330
- border-radius: var(--border-radius);
331
- border: 1px solid rgba(255, 255, 255, 0.05);
407
+ /* Main Content Area */
408
+ .main-area {
409
+ background: var(--bg-tertiary);
332
410
  display: flex;
333
411
  flex-direction: column;
334
412
  overflow: hidden;
335
413
  }
336
414
 
337
- .panel-header {
338
- padding: 20px 24px;
339
- border-bottom: 1px solid rgba(255, 255, 255, 0.05);
415
+ .main-header {
416
+ padding: 16px 24px;
417
+ border-bottom: 1px solid rgba(255,255,255,0.05);
340
418
  display: flex;
341
419
  align-items: center;
342
420
  justify-content: space-between;
343
421
  }
344
422
 
345
- .channel-title {
423
+ .main-header h3 {
424
+ font-size: 16px;
425
+ font-weight: 600;
346
426
  display: flex;
347
427
  align-items: center;
348
- gap: 12px;
349
- }
350
-
351
- .channel-title h2 {
352
- font-size: 18px;
353
- font-weight: 600;
354
- color: var(--text-primary);
428
+ gap: 8px;
355
429
  }
356
430
 
357
- .channel-title .online-status {
431
+ .online-dot {
358
432
  width: 8px;
359
433
  height: 8px;
360
434
  background: #22c55e;
361
435
  border-radius: 50%;
362
436
  }
363
437
 
364
- .search-box {
365
- display: flex;
366
- gap: 8px;
367
- }
368
-
369
- .search-box input {
370
- width: 250px;
371
- padding: 10px 16px;
372
- background: var(--bg-input);
373
- border: 1px solid rgba(255, 255, 255, 0.1);
374
- border-radius: 8px;
375
- color: var(--text-primary);
376
- font-size: 13px;
377
- font-family: inherit;
378
- transition: var(--transition);
379
- }
380
-
381
- .search-box input:focus {
382
- outline: none;
383
- border-color: var(--accent);
384
- box-shadow: 0 0 0 3px var(--accent-glow);
385
- }
386
-
387
- .search-box input::placeholder {
388
- color: var(--text-muted);
389
- }
390
-
391
- .search-box button {
392
- padding: 10px 20px;
393
- background: var(--danger);
394
- color: white;
395
- border: none;
396
- border-radius: 8px;
397
- cursor: pointer;
398
- font-size: 13px;
399
- font-weight: 500;
400
- font-family: inherit;
401
- transition: var(--transition);
402
- }
403
-
404
- .search-box button:hover {
405
- background: #d63d56;
406
- transform: translateY(-1px);
407
- }
408
-
409
- /* Messages */
410
- .messages {
438
+ .messages-container {
411
439
  flex: 1;
412
440
  overflow-y: auto;
413
- padding: 24px;
414
- }
415
-
416
- .messages::-webkit-scrollbar {
417
- width: 6px;
418
- }
419
-
420
- .messages::-webkit-scrollbar-thumb {
421
- background: rgba(255, 255, 255, 0.1);
422
- border-radius: 3px;
441
+ padding: 20px 24px;
423
442
  }
424
443
 
425
444
  .message {
426
445
  display: flex;
427
- gap: 16px;
428
- padding: 16px;
429
- border-radius: 12px;
446
+ gap: 14px;
447
+ padding: 12px;
448
+ border-radius: 10px;
430
449
  margin-bottom: 8px;
431
450
  transition: var(--transition);
432
- animation: fadeIn 0.3s ease;
433
451
  }
434
452
 
435
- @keyframes fadeIn {
436
- from { opacity: 0; transform: translateY(10px); }
437
- to { opacity: 1; transform: translateY(0); }
453
+ .message:hover {
454
+ background: rgba(255,255,255,0.03);
438
455
  }
439
456
 
440
- .message:hover {
441
- background: rgba(255, 255, 255, 0.03);
457
+ .message.highlight {
458
+ background: rgba(78, 205, 196, 0.15);
459
+ border: 1px solid rgba(78, 205, 196, 0.4);
442
460
  }
443
461
 
444
462
  .message-avatar {
445
- width: 44px;
446
- height: 44px;
447
- border-radius: 12px;
463
+ width: 40px;
464
+ height: 40px;
465
+ border-radius: 10px;
448
466
  display: flex;
449
467
  align-items: center;
450
468
  justify-content: center;
451
469
  font-weight: 600;
452
- font-size: 16px;
453
470
  flex-shrink: 0;
454
471
  }
455
472
 
456
- .message-content {
457
- flex: 1;
458
- min-width: 0;
459
- }
460
-
461
- .message-header {
462
- display: flex;
463
- align-items: baseline;
464
- gap: 12px;
465
- margin-bottom: 6px;
466
- }
467
-
468
- .message-user {
469
- font-weight: 600;
470
- font-size: 14px;
471
- color: var(--accent);
472
- }
473
-
474
- .message-time {
475
- font-size: 12px;
476
- color: var(--text-muted);
477
- }
478
-
479
- .message-text {
480
- font-size: 14px;
481
- color: var(--text-secondary);
482
- line-height: 1.6;
483
- white-space: pre-wrap;
484
- }
473
+ .message-content { flex: 1; }
474
+ .message-header { display: flex; align-items: baseline; gap: 10px; margin-bottom: 4px; }
475
+ .message-user { font-weight: 600; font-size: 14px; color: var(--accent); }
476
+ .message-time { font-size: 12px; color: var(--text-muted); }
477
+ .message-text { font-size: 14px; color: var(--text-secondary); line-height: 1.5; }
485
478
 
486
479
  .message-text code {
487
- background: rgba(0, 0, 0, 0.3);
480
+ background: rgba(0,0,0,0.3);
488
481
  padding: 2px 6px;
489
482
  border-radius: 4px;
490
- font-family: 'SF Mono', 'Fira Code', monospace;
491
- font-size: 13px;
492
483
  color: #f472b6;
484
+ font-family: monospace;
493
485
  }
494
486
 
495
- .message-text pre {
496
- background: rgba(0, 0, 0, 0.4);
497
- padding: 12px 16px;
498
- border-radius: 8px;
499
- margin: 8px 0;
500
- overflow-x: auto;
501
- border-left: 3px solid var(--accent);
487
+ /* User Profile Card */
488
+ .user-card {
489
+ background: var(--bg-secondary);
490
+ border-radius: 12px;
491
+ padding: 24px;
492
+ text-align: center;
493
+ max-width: 300px;
494
+ margin: 40px auto;
495
+ border: 1px solid rgba(255,255,255,0.1);
496
+ display: none;
502
497
  }
503
498
 
504
- .message-text pre code {
505
- background: none;
506
- padding: 0;
507
- color: #e2e8f0;
499
+ .user-card.visible {
508
500
  display: block;
509
- white-space: pre;
510
- }
511
-
512
- .message-reactions {
513
- display: flex;
514
- gap: 8px;
515
- margin-top: 10px;
501
+ animation: fadeIn 0.4s ease;
516
502
  }
517
503
 
518
- .reaction {
519
- background: rgba(255, 255, 255, 0.05);
520
- padding: 4px 10px;
521
- border-radius: 16px;
522
- font-size: 13px;
504
+ .user-card .profile-avatar {
505
+ width: 80px;
506
+ height: 80px;
507
+ border-radius: 20px;
508
+ margin: 0 auto 16px;
523
509
  display: flex;
524
510
  align-items: center;
525
- gap: 6px;
526
- cursor: pointer;
527
- transition: var(--transition);
511
+ justify-content: center;
512
+ font-size: 32px;
513
+ font-weight: 700;
514
+ background: linear-gradient(135deg, #8b5cf6, #ec4899);
528
515
  }
529
516
 
530
- .reaction:hover {
531
- background: rgba(255, 255, 255, 0.1);
517
+ .user-card h4 {
518
+ font-size: 20px;
519
+ margin-bottom: 4px;
532
520
  }
533
521
 
534
- .reaction-count {
522
+ .user-card .title {
535
523
  color: var(--text-muted);
536
- font-size: 12px;
537
- }
538
-
539
- .thread-indicator {
540
- display: flex;
541
- align-items: center;
542
- gap: 8px;
543
- margin-top: 12px;
544
- padding: 8px 12px;
545
- background: rgba(78, 205, 196, 0.1);
546
- border-radius: 8px;
547
- font-size: 13px;
548
- color: var(--accent);
549
- cursor: pointer;
550
- transition: var(--transition);
551
- width: fit-content;
552
- }
553
-
554
- .thread-indicator:hover {
555
- background: rgba(78, 205, 196, 0.15);
524
+ font-size: 14px;
525
+ margin-bottom: 16px;
556
526
  }
557
527
 
558
- /* Send box */
559
- .send-box {
560
- padding: 20px 24px;
561
- border-top: 1px solid rgba(255, 255, 255, 0.05);
528
+ .user-card .stats {
562
529
  display: flex;
563
- gap: 12px;
530
+ justify-content: center;
531
+ gap: 24px;
532
+ padding-top: 16px;
533
+ border-top: 1px solid rgba(255,255,255,0.1);
564
534
  }
565
535
 
566
- .send-box input {
567
- flex: 1;
568
- padding: 14px 20px;
569
- background: var(--bg-input);
570
- border: 1px solid rgba(255, 255, 255, 0.1);
571
- border-radius: 12px;
572
- color: var(--text-primary);
573
- font-size: 14px;
574
- font-family: inherit;
575
- transition: var(--transition);
536
+ .user-card .stat {
537
+ text-align: center;
576
538
  }
577
539
 
578
- .send-box input:focus {
579
- outline: none;
580
- border-color: var(--accent);
581
- box-shadow: 0 0 0 3px var(--accent-glow);
540
+ .user-card .stat-value {
541
+ font-size: 20px;
542
+ font-weight: 700;
543
+ color: var(--accent);
582
544
  }
583
545
 
584
- .send-box input::placeholder {
546
+ .user-card .stat-label {
547
+ font-size: 11px;
585
548
  color: var(--text-muted);
549
+ text-transform: uppercase;
586
550
  }
587
551
 
588
- .send-box button {
589
- padding: 14px 28px;
590
- background: linear-gradient(135deg, var(--accent), #3b82f6);
591
- color: var(--bg-primary);
592
- border: none;
593
- border-radius: 12px;
594
- cursor: pointer;
595
- font-size: 14px;
596
- font-weight: 600;
597
- font-family: inherit;
598
- transition: var(--transition);
599
- box-shadow: 0 4px 15px var(--accent-glow);
600
- }
601
-
602
- .send-box button:hover {
603
- transform: translateY(-2px);
604
- box-shadow: 0 6px 20px var(--accent-glow);
605
- }
606
-
607
- /* Footer */
608
- .footer {
552
+ /* Empty State */
553
+ .empty-state {
609
554
  text-align: center;
610
- padding: 24px;
555
+ padding: 60px 20px;
611
556
  color: var(--text-muted);
612
- font-size: 13px;
613
557
  }
614
558
 
615
- .footer a {
616
- color: var(--accent);
617
- text-decoration: none;
618
- }
619
-
620
- .footer a:hover {
621
- text-decoration: underline;
559
+ .empty-state .icon {
560
+ font-size: 48px;
561
+ margin-bottom: 16px;
622
562
  }
623
563
 
624
- /* Mobile responsive */
625
- @media (max-width: 900px) {
626
- .grid {
564
+ /* Mobile */
565
+ @media (max-width: 1000px) {
566
+ .split-container {
627
567
  grid-template-columns: 1fr;
628
- height: auto;
629
568
  }
630
- .sidebar {
631
- max-height: 300px;
569
+ .claude-panel {
570
+ display: none;
632
571
  }
633
- .main-panel {
634
- min-height: 500px;
572
+ .dashboard-content {
573
+ grid-template-columns: 1fr;
635
574
  }
636
- .search-box input {
637
- width: 150px;
575
+ .sidebar {
576
+ display: none;
638
577
  }
639
578
  }
640
579
  </style>
641
580
  </head>
642
581
  <body>
643
- <div class="bg-gradient"></div>
582
+ <!-- Preview Banner -->
583
+ <div class="preview-banner">
584
+ STATIC PREVIEW - No real data. Run <code>npm run web</code> for live dashboard.
585
+ <a href="https://github.com/jtalk22/slack-mcp-server">View on GitHub</a>
586
+ </div>
644
587
 
645
- <div class="container">
646
- <header class="header">
647
- <div class="logo">
648
- <div class="logo-icon">S</div>
588
+ <div class="split-container">
589
+ <!-- LEFT: Claude Chat Panel -->
590
+ <div class="claude-panel">
591
+ <div class="claude-header">
592
+ <div class="claude-logo">C</div>
649
593
  <div>
650
- <h1>Slack MCP Server</h1>
651
- <div class="subtitle">Model Context Protocol for Slack</div>
594
+ <h2>Claude</h2>
595
+ <div class="subtitle">with Slack MCP tools</div>
652
596
  </div>
597
+ <button class="reset-btn" onclick="resetDemo()">Reset Demo</button>
653
598
  </div>
654
- <div class="demo-badge">Interactive Demo</div>
655
- </header>
656
599
 
657
- <div class="status-bar">
658
- <div class="status-indicator">
659
- <div class="status-dot"></div>
660
- <span class="status-text">Connected as <strong>alex.chen</strong> @ <strong>Acme Corp</strong></span>
600
+ <div class="claude-chat" id="claudeChat">
601
+ <div class="chat-message claude">
602
+ <div class="bubble">
603
+ Hi! I have access to your Slack workspace through MCP tools. I can search messages, list conversations, look up users, and more.
604
+ <br><br>
605
+ Try one of the scenarios below to see how it works!
606
+ </div>
607
+ </div>
661
608
  </div>
662
- <div class="workspace-info">
663
- <span>12 conversations</span>
664
- <span>|</span>
665
- <span>Last sync: just now</span>
609
+
610
+ <div class="try-buttons">
611
+ <h4>Try a Scenario</h4>
612
+ <div class="scenarios">
613
+ <button class="scenario-btn" onclick="runScenario('findKey')" id="btn-findKey">
614
+ <span class="icon">🔑</span>
615
+ <div class="text">
616
+ <div class="title">Find the API Key</div>
617
+ <div class="desc">Search DMs for sensitive information</div>
618
+ </div>
619
+ </button>
620
+ <button class="scenario-btn" onclick="runScenario('listChannels')" id="btn-listChannels">
621
+ <span class="icon">📋</span>
622
+ <div class="text">
623
+ <div class="title">List Channels</div>
624
+ <div class="desc">Get all workspace channels</div>
625
+ </div>
626
+ </button>
627
+ <button class="scenario-btn" onclick="runScenario('whoIsAlex')" id="btn-whoIsAlex">
628
+ <span class="icon">👤</span>
629
+ <div class="text">
630
+ <div class="title">Who is Alex?</div>
631
+ <div class="desc">Look up user profile and activity</div>
632
+ </div>
633
+ </button>
634
+ </div>
666
635
  </div>
667
636
  </div>
668
637
 
669
- <div class="grid">
670
- <aside class="sidebar">
671
- <div class="sidebar-header">
672
- <h3>Conversations</h3>
673
- <div class="tabs">
674
- <button class="active" onclick="showDMs()">DMs</button>
675
- <button onclick="showChannels()">Channels</button>
676
- </div>
638
+ <!-- RIGHT: Slack Dashboard -->
639
+ <div class="dashboard-panel">
640
+ <div class="dashboard-header">
641
+ <div class="dashboard-title">
642
+ <h2>Slack Dashboard</h2>
643
+ <span class="status-badge">Connected</span>
677
644
  </div>
678
- <ul id="conversationList" class="conversation-list">
679
- <!-- Populated by JS -->
680
- </ul>
681
- </aside>
682
-
683
- <main class="main-panel">
684
- <div class="panel-header">
685
- <div class="channel-title">
686
- <h2 id="channelName">Sarah Johnson</h2>
687
- <div class="online-status"></div>
688
- </div>
689
- <div class="search-box">
690
- <input type="text" placeholder="Search messages..." id="searchQuery">
691
- <button onclick="searchDemo()">Search</button>
692
- </div>
693
- </div>
694
-
695
- <div id="messages" class="messages">
696
- <!-- Populated by JS -->
645
+ <div style="color: var(--text-muted); font-size: 13px;">
646
+ alex.chen @ Acme Corp
697
647
  </div>
648
+ </div>
698
649
 
699
- <div class="send-box">
700
- <input type="text" placeholder="Type a message..." id="messageInput">
701
- <button onclick="sendDemo()">Send</button>
702
- </div>
703
- </main>
650
+ <div class="dashboard-content">
651
+ <aside class="sidebar">
652
+ <div class="sidebar-section">
653
+ <h3>Conversations</h3>
654
+ </div>
655
+ <ul class="conversation-list" id="conversationList">
656
+ <!-- Populated by JS -->
657
+ </ul>
658
+ </aside>
659
+
660
+ <main class="main-area">
661
+ <div class="main-header">
662
+ <h3 id="mainTitle"><span class="online-dot"></span> Select a conversation</h3>
663
+ </div>
664
+ <div class="messages-container" id="messagesContainer">
665
+ <div class="empty-state">
666
+ <div class="icon">💬</div>
667
+ <div>Click a scenario to see Claude interact with your Slack data</div>
668
+ </div>
669
+ <div class="user-card" id="userCard">
670
+ <div class="profile-avatar" id="userAvatar">AC</div>
671
+ <h4 id="userName">Alex Chen</h4>
672
+ <div class="title" id="userTitle">Senior Engineer</div>
673
+ <div class="stats">
674
+ <div class="stat">
675
+ <div class="stat-value" id="userMessages">847</div>
676
+ <div class="stat-label">Messages</div>
677
+ </div>
678
+ <div class="stat">
679
+ <div class="stat-value" id="userChannels">12</div>
680
+ <div class="stat-label">Channels</div>
681
+ </div>
682
+ </div>
683
+ </div>
684
+ </div>
685
+ </main>
686
+ </div>
704
687
  </div>
705
-
706
- <footer class="footer">
707
- This is an interactive demo with mock data. <a href="https://github.com/jtalk22/slack-mcp-server">View on GitHub</a> to set up your own instance.
708
- </footer>
709
688
  </div>
710
689
 
711
690
  <script>
712
- // Mock data for demo
713
- const mockDMs = [
714
- { id: 'D001', name: 'Sarah Johnson', type: 'dm', unread: 3, initials: 'SJ', avatarClass: 'dm' },
715
- { id: 'D002', name: 'Mike Chen', type: 'dm', unread: 0, initials: 'MC', avatarClass: 'dm' },
716
- { id: 'D003', name: 'Emily Davis', type: 'dm', unread: 1, initials: 'ED', avatarClass: 'dm' },
717
- { id: 'D004', name: 'Alex Rivera', type: 'dm', unread: 0, initials: 'AR', avatarClass: 'dm' },
718
- { id: 'D005', name: 'Jordan Lee', type: 'dm', unread: 0, initials: 'JL', avatarClass: 'dm' },
719
- ];
720
-
721
- const mockChannels = [
722
- { id: 'C001', name: '#engineering', type: 'channel', unread: 12, initials: '#', avatarClass: 'channel' },
723
- { id: 'C002', name: '#product', type: 'channel', unread: 0, initials: '#', avatarClass: 'channel' },
724
- { id: 'C003', name: '#random', type: 'channel', unread: 5, initials: '#', avatarClass: 'channel' },
725
- { id: 'C004', name: '#announcements', type: 'channel', unread: 0, initials: '#', avatarClass: 'channel' },
726
- ];
727
-
728
- const mockMessages = {
729
- 'D001': [
730
- { user: 'Sarah Johnson', time: '10:32 AM', text: 'Hey! Did you get a chance to review the PR I submitted yesterday?', initials: 'SJ', reactions: [{ emoji: '1', count: 1 }] },
731
- { user: 'You', time: '10:35 AM', text: 'Yes! Just finished reviewing it. Left a few comments but overall looks great. The new caching layer should really help with performance.', initials: 'AC', reactions: [] },
732
- { user: 'Sarah Johnson', time: '10:38 AM', text: 'Awesome, thanks for the quick turnaround! I\'ll address those comments now.', initials: 'SJ', reactions: [{ emoji: '+1', count: 1 }] },
733
- { user: 'Sarah Johnson', time: '10:42 AM', text: 'Quick question - for the `fetchUserData` function, should I use `async/await` or stick with promises?', initials: 'SJ', reactions: [], hasThread: true, threadCount: 4 },
734
- { user: 'You', time: '10:45 AM', text: 'I\'d go with async/await - it\'s more readable and matches the rest of our codebase. Here\'s an example:\n\n```javascript\nasync function fetchUserData(userId) {\n const response = await api.get(`/users/${userId}`);\n return response.data;\n}\n```', initials: 'AC', reactions: [{ emoji: 'heart', count: 2 }] },
735
- { user: 'Sarah Johnson', time: '10:48 AM', text: 'Perfect, that makes sense. I\'ll update the PR with these changes. Should be ready for another review in about an hour.', initials: 'SJ', reactions: [] },
691
+ // ========== MOCK DATA ==========
692
+ const mockData = {
693
+ dms: [
694
+ { id: 'D001', name: 'Sarah Johnson', initials: 'SJ', preview: 'The API key is sk-abc123...', type: 'dm' },
695
+ { id: 'D002', name: 'Mike Chen', initials: 'MC', preview: 'Are we still on for 2pm?', type: 'dm' },
696
+ { id: 'D003', name: 'Alex Chen', initials: 'AC', preview: 'I pushed the fix to main', type: 'dm' },
697
+ { id: 'D004', name: 'Emily Davis', initials: 'ED', preview: 'Thanks for the review!', type: 'dm' },
736
698
  ],
737
- 'D002': [
738
- { user: 'Mike Chen', time: '9:15 AM', text: 'Morning! Are we still on for the architecture review at 2pm?', initials: 'MC', reactions: [] },
739
- { user: 'You', time: '9:20 AM', text: 'Yes, confirmed! I\'ve prepared the diagrams for the new microservices approach.', initials: 'AC', reactions: [{ emoji: '+1', count: 1 }] },
740
- { user: 'Mike Chen', time: '9:22 AM', text: 'Great. Can you share those beforehand so I can review?', initials: 'MC', reactions: [] },
699
+ channels: [
700
+ { id: 'C001', name: '#engineering', initials: '#', preview: 'Deploy complete', type: 'channel' },
701
+ { id: 'C002', name: '#product', initials: '#', preview: 'Q4 roadmap finalized', type: 'channel' },
702
+ { id: 'C003', name: '#random', initials: '#', preview: 'Anyone up for lunch?', type: 'channel' },
703
+ { id: 'C004', name: '#incidents', initials: '#', preview: 'All clear', type: 'channel' },
704
+ { id: 'C005', name: '#design', initials: '#', preview: 'New mockups ready', type: 'channel' },
741
705
  ],
742
- 'C001': [
743
- { user: 'DevOps Bot', time: '8:00 AM', text: 'Deployment to staging completed successfully. All tests passing.', initials: 'DB', reactions: [{ emoji: 'rocket', count: 5 }] },
744
- { user: 'Emily Davis', time: '9:30 AM', text: 'Team, we\'re seeing some increased latency on the API endpoints. Looking into it now.', initials: 'ED', reactions: [] },
745
- { user: 'Alex Rivera', time: '9:45 AM', text: 'I can help investigate. Which endpoints specifically?', initials: 'AR', reactions: [] },
746
- { user: 'Emily Davis', time: '9:48 AM', text: 'Mainly `/api/users` and `/api/products`. P95 latency jumped from 200ms to 800ms.', initials: 'ED', reactions: [], hasThread: true, threadCount: 12 },
747
- { user: 'You', time: '10:00 AM', text: 'Just checked the database metrics - looks like we have some N+1 queries happening after yesterday\'s deploy. I\'ll submit a fix.', initials: 'AC', reactions: [{ emoji: 'eyes', count: 3 }, { emoji: '+1', count: 2 }] },
748
- ]
706
+ messages: {
707
+ 'D001': [
708
+ { user: 'Sarah Johnson', initials: 'SJ', time: '2:32 PM', text: 'Hey, can you send me the API key for the staging environment?' },
709
+ { user: 'You', initials: 'AC', time: '2:35 PM', text: 'Sure, one sec...' },
710
+ { user: 'You', initials: 'AC', time: '2:36 PM', text: 'Here it is: <code>sk-abc123-staging-key-xyz789</code>', highlight: true },
711
+ { user: 'Sarah Johnson', initials: 'SJ', time: '2:37 PM', text: 'Got it, thanks!' },
712
+ ],
713
+ 'D003': [
714
+ { user: 'Alex Chen', initials: 'AC', time: '11:20 AM', text: 'I pushed the fix to main' },
715
+ { user: 'You', initials: 'YO', time: '11:22 AM', text: 'Nice! I\'ll review it after lunch' },
716
+ { user: 'Alex Chen', initials: 'AC', time: '11:23 AM', text: 'Sounds good. Let me know if you have questions' },
717
+ ]
718
+ },
719
+ users: {
720
+ 'alex': {
721
+ name: 'Alex Chen',
722
+ initials: 'AC',
723
+ title: 'Senior Engineer',
724
+ messages: 847,
725
+ channels: 12
726
+ }
727
+ }
749
728
  };
750
729
 
751
- let currentView = 'dms';
752
- let currentChannel = 'D001';
730
+ let isRunning = false;
753
731
 
754
- function renderConversations(conversations) {
755
- const list = document.getElementById('conversationList');
756
- list.innerHTML = conversations.map(c => `
757
- <li class="conversation-item ${c.id === currentChannel ? 'active' : ''}" onclick="selectConversation('${c.id}', '${c.name}')">
758
- <div class="avatar ${c.avatarClass}">${c.initials}</div>
732
+ // ========== RENDERING ==========
733
+ function renderConversations(list, highlightId = null) {
734
+ const container = document.getElementById('conversationList');
735
+ container.innerHTML = list.map(c => `
736
+ <li class="conversation-item ${c.id === highlightId ? 'highlight active' : ''}" data-id="${c.id}">
737
+ <div class="avatar ${c.type}">${c.initials}</div>
759
738
  <div class="conv-info">
760
739
  <div class="conv-name">${c.name}</div>
761
- <div class="conv-type">${c.type === 'dm' ? 'Direct message' : 'Channel'}</div>
740
+ <div class="conv-preview">${c.preview}</div>
762
741
  </div>
763
- ${c.unread > 0 ? `<div class="unread-badge">${c.unread}</div>` : ''}
764
742
  </li>
765
743
  `).join('');
766
744
  }
767
745
 
768
- function renderMessages(channelId) {
769
- const container = document.getElementById('messages');
770
- const messages = mockMessages[channelId] || [];
746
+ function renderMessages(channelId, highlightIndex = -1) {
747
+ const container = document.getElementById('messagesContainer');
748
+ const messages = mockData.messages[channelId] || [];
749
+
750
+ document.getElementById('userCard').classList.remove('visible');
771
751
 
772
752
  if (messages.length === 0) {
773
- container.innerHTML = '<div style="text-align: center; padding: 40px; color: var(--text-muted);">No messages yet</div>';
753
+ container.innerHTML = '<div class="empty-state"><div class="icon">💬</div><div>No messages</div></div>';
774
754
  return;
775
755
  }
776
756
 
777
757
  container.innerHTML = messages.map((m, i) => `
778
- <div class="message" style="animation-delay: ${i * 0.05}s">
779
- <div class="message-avatar" style="background: linear-gradient(135deg, ${getAvatarColor(m.initials)})">${m.initials}</div>
758
+ <div class="message ${m.highlight || i === highlightIndex ? 'highlight' : ''}">
759
+ <div class="message-avatar" style="background: linear-gradient(135deg, ${getColor(m.initials)})">${m.initials}</div>
780
760
  <div class="message-content">
781
761
  <div class="message-header">
782
762
  <span class="message-user">${m.user}</span>
783
763
  <span class="message-time">${m.time}</span>
784
764
  </div>
785
- <div class="message-text">${formatMessageText(m.text)}</div>
786
- ${m.reactions && m.reactions.length > 0 ? `
787
- <div class="message-reactions">
788
- ${m.reactions.map(r => `<div class="reaction">${getEmoji(r.emoji)} <span class="reaction-count">${r.count}</span></div>`).join('')}
789
- </div>
790
- ` : ''}
791
- ${m.hasThread ? `
792
- <div class="thread-indicator">
793
- <span>View thread</span>
794
- <span style="opacity: 0.7">${m.threadCount} replies</span>
795
- </div>
796
- ` : ''}
765
+ <div class="message-text">${m.text}</div>
797
766
  </div>
798
767
  </div>
799
768
  `).join('');
800
-
801
- container.scrollTop = container.scrollHeight;
802
769
  }
803
770
 
804
- function formatMessageText(text) {
805
- // Convert multiline code blocks to <pre><code>
806
- text = text.replace(/```(\w+)?\n([\s\S]*?)```/g, '<pre><code>$2</code></pre>');
807
- // Convert inline code to <code>
808
- text = text.replace(/`([^`]+)`/g, '<code>$1</code>');
809
- return text;
810
- }
771
+ function showUserCard(userId) {
772
+ const user = mockData.users[userId];
773
+ if (!user) return;
811
774
 
812
- function getEmoji(name) {
813
- const emojis = {
814
- '+1': '👍',
815
- 'heart': '❤️',
816
- 'rocket': '🚀',
817
- 'eyes': '👀',
818
- '1': '👍',
819
- 'fire': '🔥',
820
- 'tada': '🎉',
821
- 'thinking': '🤔'
822
- };
823
- return emojis[name] || name;
775
+ document.getElementById('messagesContainer').innerHTML = '';
776
+ document.getElementById('userAvatar').textContent = user.initials;
777
+ document.getElementById('userName').textContent = user.name;
778
+ document.getElementById('userTitle').textContent = user.title;
779
+ document.getElementById('userMessages').textContent = user.messages;
780
+ document.getElementById('userChannels').textContent = user.channels;
781
+ document.getElementById('userCard').classList.add('visible');
824
782
  }
825
783
 
826
- function getAvatarColor(initials) {
784
+ function getColor(initials) {
827
785
  const colors = [
828
- ['#8b5cf6', '#ec4899'],
829
- ['#3b82f6', '#06b6d4'],
830
- ['#f59e0b', '#ef4444'],
831
- ['#10b981', '#3b82f6'],
832
- ['#f472b6', '#8b5cf6']
786
+ '#8b5cf6, #ec4899',
787
+ '#3b82f6, #06b6d4',
788
+ '#f59e0b, #ef4444',
789
+ '#10b981, #3b82f6'
833
790
  ];
834
- const index = initials.charCodeAt(0) % colors.length;
835
- return colors[index].join(', ');
791
+ return colors[initials.charCodeAt(0) % colors.length];
792
+ }
793
+
794
+ // ========== CLAUDE CHAT ==========
795
+ function addUserMessage(text) {
796
+ const chat = document.getElementById('claudeChat');
797
+ chat.innerHTML += `
798
+ <div class="chat-message user">
799
+ <div class="bubble">${text}</div>
800
+ </div>
801
+ `;
802
+ chat.scrollTop = chat.scrollHeight;
803
+ }
804
+
805
+ function addClaudeMessage(html) {
806
+ const chat = document.getElementById('claudeChat');
807
+ chat.innerHTML += `
808
+ <div class="chat-message claude">
809
+ <div class="bubble">${html}</div>
810
+ </div>
811
+ `;
812
+ chat.scrollTop = chat.scrollHeight;
813
+ }
814
+
815
+ function addToolCall(toolName, params) {
816
+ const chat = document.getElementById('claudeChat');
817
+ chat.innerHTML += `
818
+ <div class="chat-message claude">
819
+ <div class="bubble">
820
+ <div class="tool-call">
821
+ <div class="tool-name">${toolName}</div>
822
+ <div class="tool-params">${params}</div>
823
+ </div>
824
+ </div>
825
+ </div>
826
+ `;
827
+ chat.scrollTop = chat.scrollHeight;
836
828
  }
837
829
 
838
- function showDMs() {
839
- currentView = 'dms';
840
- document.querySelectorAll('.tabs button').forEach(b => b.classList.remove('active'));
841
- event.target.classList.add('active');
842
- renderConversations(mockDMs);
830
+ function showTyping() {
831
+ const chat = document.getElementById('claudeChat');
832
+ const id = 'typing-' + Date.now();
833
+ chat.innerHTML += `
834
+ <div class="chat-message claude" id="${id}">
835
+ <div class="bubble">
836
+ <div class="typing-indicator">
837
+ <span></span><span></span><span></span>
838
+ </div>
839
+ </div>
840
+ </div>
841
+ `;
842
+ chat.scrollTop = chat.scrollHeight;
843
+ return id;
843
844
  }
844
845
 
845
- function showChannels() {
846
- currentView = 'channels';
847
- document.querySelectorAll('.tabs button').forEach(b => b.classList.remove('active'));
848
- event.target.classList.add('active');
849
- renderConversations(mockChannels);
846
+ function removeTyping(id) {
847
+ const el = document.getElementById(id);
848
+ if (el) el.remove();
850
849
  }
851
850
 
852
- function selectConversation(id, name) {
853
- currentChannel = id;
854
- document.getElementById('channelName').textContent = name;
851
+ // ========== SCENARIOS ==========
852
+ async function runScenario(scenario) {
853
+ if (isRunning) return;
854
+ isRunning = true;
855
+
856
+ // Disable all buttons
857
+ document.querySelectorAll('.scenario-btn').forEach(btn => btn.disabled = true);
855
858
 
856
- // Update active state
857
- document.querySelectorAll('.conversation-item').forEach(li => li.classList.remove('active'));
858
- event.target.closest('.conversation-item').classList.add('active');
859
+ switch (scenario) {
860
+ case 'findKey':
861
+ await scenarioFindKey();
862
+ break;
863
+ case 'listChannels':
864
+ await scenarioListChannels();
865
+ break;
866
+ case 'whoIsAlex':
867
+ await scenarioWhoIsAlex();
868
+ break;
869
+ }
859
870
 
860
- renderMessages(id);
871
+ // Re-enable buttons
872
+ document.querySelectorAll('.scenario-btn').forEach(btn => btn.disabled = false);
873
+ isRunning = false;
861
874
  }
862
875
 
863
- function searchDemo() {
864
- const query = document.getElementById('searchQuery').value;
865
- if (!query) return;
876
+ async function scenarioFindKey() {
877
+ // User asks
878
+ addUserMessage("Can you find the API key that Sarah sent me?");
879
+ await delay(500);
866
880
 
867
- document.getElementById('channelName').textContent = `Search: "${query}"`;
868
- document.getElementById('messages').innerHTML = `
869
- <div class="message">
870
- <div class="message-avatar" style="background: linear-gradient(135deg, #8b5cf6, #ec4899)">SJ</div>
871
- <div class="message-content">
872
- <div class="message-header">
873
- <span class="message-user">Sarah Johnson</span>
874
- <span class="message-time">Yesterday at 3:42 PM</span>
875
- </div>
876
- <div class="message-text">Found 3 results for "<strong>${query}</strong>" across your conversations...</div>
877
- </div>
881
+ // Claude thinks
882
+ const typing1 = showTyping();
883
+ await delay(600);
884
+ removeTyping(typing1);
885
+
886
+ // Tool call
887
+ addToolCall('slack_search_messages', '{ query: "API key", count: 10 }');
888
+ await delay(900);
889
+
890
+ // Dashboard updates - show DMs with Sarah highlighted
891
+ renderConversations(mockData.dms, 'D001');
892
+ document.getElementById('mainTitle').innerHTML = '<span class="online-dot"></span> Sarah Johnson';
893
+ await delay(300);
894
+
895
+ // Show messages with the key highlighted
896
+ renderMessages('D001');
897
+ await delay(600);
898
+
899
+ // Claude responds
900
+ const typing2 = showTyping();
901
+ await delay(500);
902
+ removeTyping(typing2);
903
+
904
+ addClaudeMessage(`Found it! Sarah asked for the staging API key on your DM. You sent her:<br><br><code>sk-abc123-staging-key-xyz789</code><br><br>This was shared at 2:36 PM today.`);
905
+ }
906
+
907
+ async function scenarioListChannels() {
908
+ addUserMessage("What channels am I in?");
909
+ await delay(500);
910
+
911
+ const typing1 = showTyping();
912
+ await delay(600);
913
+ removeTyping(typing1);
914
+
915
+ addToolCall('slack_list_conversations', '{ types: "public_channel,private_channel" }');
916
+ await delay(900);
917
+
918
+ // Dashboard updates to show channels
919
+ renderConversations(mockData.channels);
920
+ document.getElementById('mainTitle').innerHTML = 'Channels';
921
+ document.getElementById('messagesContainer').innerHTML = `
922
+ <div class="empty-state">
923
+ <div class="icon">📋</div>
924
+ <div>5 channels loaded</div>
878
925
  </div>
879
926
  `;
927
+ await delay(500);
928
+
929
+ const typing2 = showTyping();
930
+ await delay(600);
931
+ removeTyping(typing2);
932
+
933
+ addClaudeMessage(`You're a member of <strong>5 channels</strong>:<br><br>
934
+ • <strong>#engineering</strong> - Latest: "Deploy complete"<br>
935
+ • <strong>#product</strong> - Q4 roadmap discussions<br>
936
+ • <strong>#random</strong> - Social chat<br>
937
+ • <strong>#incidents</strong> - On-call alerts<br>
938
+ • <strong>#design</strong> - Design team updates<br><br>
939
+ The most active appears to be #engineering.`);
880
940
  }
881
941
 
882
- function sendDemo() {
883
- const input = document.getElementById('messageInput');
884
- const text = input.value.trim();
885
- if (!text) return;
942
+ async function scenarioWhoIsAlex() {
943
+ addUserMessage("Who is Alex? What does he do here?");
944
+ await delay(500);
886
945
 
887
- const container = document.getElementById('messages');
888
- const now = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
946
+ const typing1 = showTyping();
947
+ await delay(600);
948
+ removeTyping(typing1);
889
949
 
890
- container.innerHTML += `
891
- <div class="message">
892
- <div class="message-avatar" style="background: linear-gradient(135deg, #10b981, #3b82f6)">AC</div>
893
- <div class="message-content">
894
- <div class="message-header">
895
- <span class="message-user">You</span>
896
- <span class="message-time">${now}</span>
950
+ addToolCall('slack_users_info', '{ user_id: "U_ALEX" }');
951
+ await delay(600);
952
+
953
+ // Highlight Alex in DMs
954
+ renderConversations(mockData.dms, 'D003');
955
+ await delay(300);
956
+
957
+ addToolCall('slack_search_messages', '{ query: "from:alex", count: 50 }');
958
+ await delay(900);
959
+
960
+ // Show user profile card
961
+ document.getElementById('mainTitle').innerHTML = '<span class="online-dot"></span> User Profile';
962
+ showUserCard('alex');
963
+ await delay(500);
964
+
965
+ const typing2 = showTyping();
966
+ await delay(700);
967
+ removeTyping(typing2);
968
+
969
+ addClaudeMessage(`<strong>Alex Chen</strong> is a Senior Engineer on your team.<br><br>
970
+ 📊 <strong>Activity:</strong> 847 messages across 12 channels<br>
971
+ 💬 <strong>Most active in:</strong> #engineering, #incidents<br>
972
+ 🕐 <strong>Recent:</strong> Pushed a fix to main branch today<br><br>
973
+ Based on his messages, he focuses on backend infrastructure and is often involved in incident response.`);
974
+ }
975
+
976
+ function delay(ms) {
977
+ return new Promise(resolve => setTimeout(resolve, ms));
978
+ }
979
+
980
+ function resetDemo() {
981
+ // Reset chat to initial state
982
+ document.getElementById('claudeChat').innerHTML = `
983
+ <div class="chat-message claude">
984
+ <div class="bubble">
985
+ Hi! I have access to your Slack workspace through MCP tools. I can search messages, list conversations, look up users, and more.
986
+ <br><br>
987
+ Try one of the scenarios below to see how it works!
988
+ </div>
989
+ </div>
990
+ `;
991
+
992
+ // Reset dashboard
993
+ renderConversations(mockData.dms);
994
+ document.getElementById('mainTitle').innerHTML = '<span class="online-dot"></span> Select a conversation';
995
+ document.getElementById('messagesContainer').innerHTML = `
996
+ <div class="empty-state">
997
+ <div class="icon">💬</div>
998
+ <div>Click a scenario to see Claude interact with your Slack data</div>
999
+ </div>
1000
+ <div class="user-card" id="userCard">
1001
+ <div class="profile-avatar" id="userAvatar">AC</div>
1002
+ <h4 id="userName">Alex Chen</h4>
1003
+ <div class="title" id="userTitle">Senior Engineer</div>
1004
+ <div class="stats">
1005
+ <div class="stat">
1006
+ <div class="stat-value" id="userMessages">847</div>
1007
+ <div class="stat-label">Messages</div>
1008
+ </div>
1009
+ <div class="stat">
1010
+ <div class="stat-value" id="userChannels">12</div>
1011
+ <div class="stat-label">Channels</div>
897
1012
  </div>
898
- <div class="message-text">${text}</div>
899
1013
  </div>
900
1014
  </div>
901
1015
  `;
902
1016
 
903
- input.value = '';
904
- container.scrollTop = container.scrollHeight;
1017
+ isRunning = false;
905
1018
  }
906
1019
 
907
- // Initialize
908
- renderConversations(mockDMs);
909
- renderMessages('D001');
910
-
911
- // Handle enter key
912
- document.getElementById('messageInput').addEventListener('keypress', (e) => {
913
- if (e.key === 'Enter') sendDemo();
914
- });
915
- document.getElementById('searchQuery').addEventListener('keypress', (e) => {
916
- if (e.key === 'Enter') searchDemo();
917
- });
1020
+ // ========== INIT ==========
1021
+ renderConversations(mockData.dms);
918
1022
  </script>
919
1023
  </body>
920
1024
  </html>