@jtalk22/slack-mcp 1.0.6 → 1.1.1

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,968 @@
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;
243
322
  }
244
323
 
245
- .conversation-list::-webkit-scrollbar-track {
246
- background: transparent;
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;
247
331
  }
248
332
 
249
- .conversation-list::-webkit-scrollbar-thumb {
250
- background: rgba(255, 255, 255, 0.1);
251
- border-radius: 3px;
333
+ .sidebar-section {
334
+ padding: 16px;
335
+ border-bottom: 1px solid rgba(255,255,255,0.05);
336
+ }
337
+
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;
344
+ }
345
+
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
402
 
296
- .conv-info {
297
- flex: 1;
298
- min-width: 0;
299
- }
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; }
300
406
 
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
-
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
- }
326
-
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;
501
+ animation: fadeIn 0.4s ease;
510
502
  }
511
503
 
512
- .message-reactions {
513
- display: flex;
514
- gap: 8px;
515
- margin-top: 10px;
516
- }
517
-
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>
677
- </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>
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>
693
644
  </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>
670
+ </main>
671
+ </div>
704
672
  </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
673
  </div>
710
674
 
711
675
  <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: [] },
676
+ // ========== MOCK DATA ==========
677
+ const mockData = {
678
+ dms: [
679
+ { id: 'D001', name: 'Sarah Johnson', initials: 'SJ', preview: 'The API key is sk-abc123...', type: 'dm' },
680
+ { id: 'D002', name: 'Mike Chen', initials: 'MC', preview: 'Are we still on for 2pm?', type: 'dm' },
681
+ { id: 'D003', name: 'Alex Chen', initials: 'AC', preview: 'I pushed the fix to main', type: 'dm' },
682
+ { id: 'D004', name: 'Emily Davis', initials: 'ED', preview: 'Thanks for the review!', type: 'dm' },
736
683
  ],
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: [] },
684
+ channels: [
685
+ { id: 'C001', name: '#engineering', initials: '#', preview: 'Deploy complete', type: 'channel' },
686
+ { id: 'C002', name: '#product', initials: '#', preview: 'Q4 roadmap finalized', type: 'channel' },
687
+ { id: 'C003', name: '#random', initials: '#', preview: 'Anyone up for lunch?', type: 'channel' },
688
+ { id: 'C004', name: '#incidents', initials: '#', preview: 'All clear', type: 'channel' },
689
+ { id: 'C005', name: '#design', initials: '#', preview: 'New mockups ready', type: 'channel' },
741
690
  ],
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
- ]
691
+ messages: {
692
+ 'D001': [
693
+ { user: 'Sarah Johnson', initials: 'SJ', time: '2:32 PM', text: 'Hey, can you send me the API key for the staging environment?' },
694
+ { user: 'You', initials: 'AC', time: '2:35 PM', text: 'Sure, one sec...' },
695
+ { user: 'You', initials: 'AC', time: '2:36 PM', text: 'Here it is: <code>sk-abc123-staging-key-xyz789</code>', highlight: true },
696
+ { user: 'Sarah Johnson', initials: 'SJ', time: '2:37 PM', text: 'Got it, thanks!' },
697
+ ],
698
+ 'D003': [
699
+ { user: 'Alex Chen', initials: 'AC', time: '11:20 AM', text: 'I pushed the fix to main' },
700
+ { user: 'You', initials: 'YO', time: '11:22 AM', text: 'Nice! I\'ll review it after lunch' },
701
+ { user: 'Alex Chen', initials: 'AC', time: '11:23 AM', text: 'Sounds good. Let me know if you have questions' },
702
+ ]
703
+ },
704
+ users: {
705
+ 'alex': {
706
+ name: 'Alex Chen',
707
+ initials: 'AC',
708
+ title: 'Senior Engineer',
709
+ messages: 847,
710
+ channels: 12
711
+ }
712
+ }
749
713
  };
750
714
 
751
- let currentView = 'dms';
752
- let currentChannel = 'D001';
715
+ let isRunning = false;
753
716
 
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>
717
+ // ========== RENDERING ==========
718
+ function renderConversations(list, highlightId = null) {
719
+ const container = document.getElementById('conversationList');
720
+ container.innerHTML = list.map(c => `
721
+ <li class="conversation-item ${c.id === highlightId ? 'highlight active' : ''}" data-id="${c.id}">
722
+ <div class="avatar ${c.type}">${c.initials}</div>
759
723
  <div class="conv-info">
760
724
  <div class="conv-name">${c.name}</div>
761
- <div class="conv-type">${c.type === 'dm' ? 'Direct message' : 'Channel'}</div>
725
+ <div class="conv-preview">${c.preview}</div>
762
726
  </div>
763
- ${c.unread > 0 ? `<div class="unread-badge">${c.unread}</div>` : ''}
764
727
  </li>
765
728
  `).join('');
766
729
  }
767
730
 
768
- function renderMessages(channelId) {
769
- const container = document.getElementById('messages');
770
- const messages = mockMessages[channelId] || [];
731
+ function renderMessages(channelId, highlightIndex = -1) {
732
+ const container = document.getElementById('messagesContainer');
733
+ const messages = mockData.messages[channelId] || [];
771
734
 
772
735
  if (messages.length === 0) {
773
- container.innerHTML = '<div style="text-align: center; padding: 40px; color: var(--text-muted);">No messages yet</div>';
736
+ container.innerHTML = '<div class="empty-state"><div class="icon">💬</div><div>No messages</div></div>';
774
737
  return;
775
738
  }
776
739
 
777
740
  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>
741
+ <div class="message ${m.highlight || i === highlightIndex ? 'highlight' : ''}">
742
+ <div class="message-avatar" style="background: linear-gradient(135deg, ${getColor(m.initials)})">${m.initials}</div>
780
743
  <div class="message-content">
781
744
  <div class="message-header">
782
745
  <span class="message-user">${m.user}</span>
783
746
  <span class="message-time">${m.time}</span>
784
747
  </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
- ` : ''}
748
+ <div class="message-text">${m.text}</div>
797
749
  </div>
798
750
  </div>
799
751
  `).join('');
752
+ }
800
753
 
801
- container.scrollTop = container.scrollHeight;
754
+ function showUserCard(userId) {
755
+ const user = mockData.users[userId];
756
+ if (!user) return;
757
+
758
+ // Render the user card directly into the container
759
+ document.getElementById('messagesContainer').innerHTML = `
760
+ <div class="user-card visible">
761
+ <div class="profile-avatar">${user.initials}</div>
762
+ <h4>${user.name}</h4>
763
+ <div class="title">${user.title}</div>
764
+ <div class="stats">
765
+ <div class="stat">
766
+ <div class="stat-value">${user.messages}</div>
767
+ <div class="stat-label">Messages</div>
768
+ </div>
769
+ <div class="stat">
770
+ <div class="stat-value">${user.channels}</div>
771
+ <div class="stat-label">Channels</div>
772
+ </div>
773
+ </div>
774
+ </div>
775
+ `;
776
+ }
777
+
778
+ function getColor(initials) {
779
+ const colors = [
780
+ '#8b5cf6, #ec4899',
781
+ '#3b82f6, #06b6d4',
782
+ '#f59e0b, #ef4444',
783
+ '#10b981, #3b82f6'
784
+ ];
785
+ return colors[initials.charCodeAt(0) % colors.length];
802
786
  }
803
787
 
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;
788
+ // ========== CLAUDE CHAT ==========
789
+ function addUserMessage(text) {
790
+ const chat = document.getElementById('claudeChat');
791
+ chat.innerHTML += `
792
+ <div class="chat-message user">
793
+ <div class="bubble">${text}</div>
794
+ </div>
795
+ `;
796
+ chat.scrollTop = chat.scrollHeight;
810
797
  }
811
798
 
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;
799
+ function addClaudeMessage(html) {
800
+ const chat = document.getElementById('claudeChat');
801
+ chat.innerHTML += `
802
+ <div class="chat-message claude">
803
+ <div class="bubble">${html}</div>
804
+ </div>
805
+ `;
806
+ chat.scrollTop = chat.scrollHeight;
824
807
  }
825
808
 
826
- function getAvatarColor(initials) {
827
- const colors = [
828
- ['#8b5cf6', '#ec4899'],
829
- ['#3b82f6', '#06b6d4'],
830
- ['#f59e0b', '#ef4444'],
831
- ['#10b981', '#3b82f6'],
832
- ['#f472b6', '#8b5cf6']
833
- ];
834
- const index = initials.charCodeAt(0) % colors.length;
835
- return colors[index].join(', ');
809
+ function addToolCall(toolName, params) {
810
+ const chat = document.getElementById('claudeChat');
811
+ chat.innerHTML += `
812
+ <div class="chat-message claude">
813
+ <div class="bubble">
814
+ <div class="tool-call">
815
+ <div class="tool-name">${toolName}</div>
816
+ <div class="tool-params">${params}</div>
817
+ </div>
818
+ </div>
819
+ </div>
820
+ `;
821
+ chat.scrollTop = chat.scrollHeight;
836
822
  }
837
823
 
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);
824
+ function showTyping() {
825
+ const chat = document.getElementById('claudeChat');
826
+ const id = 'typing-' + Date.now();
827
+ chat.innerHTML += `
828
+ <div class="chat-message claude" id="${id}">
829
+ <div class="bubble">
830
+ <div class="typing-indicator">
831
+ <span></span><span></span><span></span>
832
+ </div>
833
+ </div>
834
+ </div>
835
+ `;
836
+ chat.scrollTop = chat.scrollHeight;
837
+ return id;
843
838
  }
844
839
 
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);
840
+ function removeTyping(id) {
841
+ const el = document.getElementById(id);
842
+ if (el) el.remove();
850
843
  }
851
844
 
852
- function selectConversation(id, name) {
853
- currentChannel = id;
854
- document.getElementById('channelName').textContent = name;
845
+ // ========== SCENARIOS ==========
846
+ async function runScenario(scenario) {
847
+ if (isRunning) return;
848
+ isRunning = true;
855
849
 
856
- // Update active state
857
- document.querySelectorAll('.conversation-item').forEach(li => li.classList.remove('active'));
858
- event.target.closest('.conversation-item').classList.add('active');
850
+ // Disable all buttons
851
+ document.querySelectorAll('.scenario-btn').forEach(btn => btn.disabled = true);
859
852
 
860
- renderMessages(id);
853
+ switch (scenario) {
854
+ case 'findKey':
855
+ await scenarioFindKey();
856
+ break;
857
+ case 'listChannels':
858
+ await scenarioListChannels();
859
+ break;
860
+ case 'whoIsAlex':
861
+ await scenarioWhoIsAlex();
862
+ break;
863
+ }
864
+
865
+ // Re-enable buttons
866
+ document.querySelectorAll('.scenario-btn').forEach(btn => btn.disabled = false);
867
+ isRunning = false;
861
868
  }
862
869
 
863
- function searchDemo() {
864
- const query = document.getElementById('searchQuery').value;
865
- if (!query) return;
870
+ async function scenarioFindKey() {
871
+ // User asks
872
+ addUserMessage("Can you find the API key that Sarah sent me?");
873
+ await delay(500);
866
874
 
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>
875
+ // Claude thinks
876
+ const typing1 = showTyping();
877
+ await delay(600);
878
+ removeTyping(typing1);
879
+
880
+ // Tool call
881
+ addToolCall('slack_search_messages', '{ query: "API key", count: 10 }');
882
+ await delay(900);
883
+
884
+ // Dashboard updates - show DMs with Sarah highlighted
885
+ renderConversations(mockData.dms, 'D001');
886
+ document.getElementById('mainTitle').innerHTML = '<span class="online-dot"></span> Sarah Johnson';
887
+ await delay(300);
888
+
889
+ // Show messages with the key highlighted
890
+ renderMessages('D001');
891
+ await delay(600);
892
+
893
+ // Claude responds
894
+ const typing2 = showTyping();
895
+ await delay(500);
896
+ removeTyping(typing2);
897
+
898
+ 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.`);
899
+ }
900
+
901
+ async function scenarioListChannels() {
902
+ addUserMessage("What channels am I in?");
903
+ await delay(500);
904
+
905
+ const typing1 = showTyping();
906
+ await delay(600);
907
+ removeTyping(typing1);
908
+
909
+ addToolCall('slack_list_conversations', '{ types: "public_channel,private_channel" }');
910
+ await delay(900);
911
+
912
+ // Dashboard updates to show channels
913
+ renderConversations(mockData.channels);
914
+ document.getElementById('mainTitle').innerHTML = 'Channels';
915
+ document.getElementById('messagesContainer').innerHTML = `
916
+ <div class="empty-state">
917
+ <div class="icon">📋</div>
918
+ <div>5 channels loaded</div>
878
919
  </div>
879
920
  `;
921
+ await delay(500);
922
+
923
+ const typing2 = showTyping();
924
+ await delay(600);
925
+ removeTyping(typing2);
926
+
927
+ addClaudeMessage(`You're a member of <strong>5 channels</strong>:<br><br>
928
+ • <strong>#engineering</strong> - Latest: "Deploy complete"<br>
929
+ • <strong>#product</strong> - Q4 roadmap discussions<br>
930
+ • <strong>#random</strong> - Social chat<br>
931
+ • <strong>#incidents</strong> - On-call alerts<br>
932
+ • <strong>#design</strong> - Design team updates<br><br>
933
+ The most active appears to be #engineering.`);
880
934
  }
881
935
 
882
- function sendDemo() {
883
- const input = document.getElementById('messageInput');
884
- const text = input.value.trim();
885
- if (!text) return;
936
+ async function scenarioWhoIsAlex() {
937
+ addUserMessage("Who is Alex? What does he do here?");
938
+ await delay(500);
886
939
 
887
- const container = document.getElementById('messages');
888
- const now = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
940
+ const typing1 = showTyping();
941
+ await delay(600);
942
+ removeTyping(typing1);
889
943
 
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>
897
- </div>
898
- <div class="message-text">${text}</div>
944
+ addToolCall('slack_users_info', '{ user_id: "U_ALEX" }');
945
+ await delay(600);
946
+
947
+ // Highlight Alex in DMs
948
+ renderConversations(mockData.dms, 'D003');
949
+ await delay(300);
950
+
951
+ addToolCall('slack_search_messages', '{ query: "from:alex", count: 50 }');
952
+ await delay(900);
953
+
954
+ // Show user profile card
955
+ document.getElementById('mainTitle').innerHTML = '<span class="online-dot"></span> User Profile';
956
+ showUserCard('alex');
957
+ await delay(500);
958
+
959
+ const typing2 = showTyping();
960
+ await delay(700);
961
+ removeTyping(typing2);
962
+
963
+ addClaudeMessage(`<strong>Alex Chen</strong> is a Senior Engineer on your team.<br><br>
964
+ 📊 <strong>Activity:</strong> 847 messages across 12 channels<br>
965
+ 💬 <strong>Most active in:</strong> #engineering, #incidents<br>
966
+ 🕐 <strong>Recent:</strong> Pushed a fix to main branch today<br><br>
967
+ Based on his messages, he focuses on backend infrastructure and is often involved in incident response.`);
968
+ }
969
+
970
+ function delay(ms) {
971
+ return new Promise(resolve => setTimeout(resolve, ms));
972
+ }
973
+
974
+ function resetDemo() {
975
+ // Reset chat to initial state
976
+ document.getElementById('claudeChat').innerHTML = `
977
+ <div class="chat-message claude">
978
+ <div class="bubble">
979
+ Hi! I have access to your Slack workspace through MCP tools. I can search messages, list conversations, look up users, and more.
980
+ <br><br>
981
+ Try one of the scenarios below to see how it works!
899
982
  </div>
900
983
  </div>
901
984
  `;
902
985
 
903
- input.value = '';
904
- container.scrollTop = container.scrollHeight;
905
- }
986
+ // Reset dashboard
987
+ renderConversations(mockData.dms);
988
+ document.getElementById('mainTitle').innerHTML = '<span class="online-dot"></span> Select a conversation';
989
+ document.getElementById('messagesContainer').innerHTML = `
990
+ <div class="empty-state">
991
+ <div class="icon">💬</div>
992
+ <div>Click a scenario to see Claude interact with your Slack data</div>
993
+ </div>
994
+ `;
906
995
 
907
- // Initialize
908
- renderConversations(mockDMs);
909
- renderMessages('D001');
996
+ isRunning = false;
997
+ }
910
998
 
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
- });
999
+ // ========== INIT ==========
1000
+ renderConversations(mockData.dms);
918
1001
  </script>
919
1002
  </body>
920
1003
  </html>