@jtalk22/slack-mcp 1.1.9 → 1.2.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.
@@ -0,0 +1,1838 @@
1
+ <!--
2
+ Slack MCP Server - Claude Desktop Demo
3
+ Author: @jtalk22
4
+ Repository: https://github.com/jtalk22/slack-mcp-server
5
+ License: MIT
6
+ Created: January 2026
7
+ -->
8
+ <!DOCTYPE html>
9
+ <html lang="en">
10
+ <head>
11
+ <meta charset="UTF-8">
12
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
13
+ <meta name="author" content="@jtalk22">
14
+ <title>Slack MCP Server - Claude Desktop Demo</title>
15
+ <meta name="description" content="See how Claude uses Slack MCP tools to access your workspace - DMs, channels, search, and more.">
16
+
17
+ <!-- Open Graph -->
18
+ <meta property="og:title" content="Slack MCP Server - Claude Desktop Demo">
19
+ <meta property="og:description" content="Interactive demo showing Claude Desktop using MCP tools to access Slack - search messages, read threads, send updates.">
20
+ <meta property="og:type" content="website">
21
+ <meta property="og:url" content="https://jtalk22.github.io/slack-mcp-server/public/demo-claude.html">
22
+ <meta property="og:image" content="https://assets-worker.james-20a.workers.dev/projects/slack-mcp-server/demo-claude.gif">
23
+
24
+ <!-- Twitter Card -->
25
+ <meta name="twitter:card" content="summary_large_image">
26
+ <meta name="twitter:title" content="Slack MCP Server - Claude Desktop Demo">
27
+ <meta name="twitter:description" content="Interactive demo showing Claude using MCP tools to access your Slack workspace.">
28
+ <meta name="twitter:image" content="https://assets-worker.james-20a.workers.dev/projects/slack-mcp-server/demo-claude.gif">
29
+
30
+ <!-- Theme -->
31
+ <meta name="theme-color" content="#1a1a1a">
32
+ <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>💬</text></svg>">
33
+ <style>
34
+ /* ═══════════════════════════════════════════════════════════════
35
+ Claude Desktop Color Palette (Dark Mode)
36
+ ═══════════════════════════════════════════════════════════════ */
37
+ :root {
38
+ /* Window chrome */
39
+ --window-bg: #1a1a1a;
40
+ --window-chrome: #2d2d2d;
41
+ --window-border: #3a3a3a;
42
+ --traffic-red: #ff5f57;
43
+ --traffic-yellow: #febc2e;
44
+ --traffic-green: #28c840;
45
+
46
+ /* Messages */
47
+ --user-bubble-bg: #3b3b3b;
48
+ --claude-bubble-bg: #2a2a2a;
49
+ --claude-orange: #da7756;
50
+
51
+ /* Tool calls */
52
+ --tool-box-bg: #1f1f1f;
53
+ --tool-box-border: #3a3a3a;
54
+ --tool-header-bg: #252525;
55
+ --tool-name-color: #a0a0a0;
56
+
57
+ /* Text */
58
+ --text-primary: #ffffff;
59
+ --text-secondary: #b0b0b0;
60
+ --text-muted: #666666;
61
+
62
+ /* Accents */
63
+ --link-color: #6eb5ff;
64
+ --code-bg: #2d2d2d;
65
+ --code-text: #e6e6e6;
66
+ --success-color: #28c840;
67
+ --warning-color: #febc2e;
68
+
69
+ /* Brand DNA (subliminal) */
70
+ --text-warm: #E8E4DF;
71
+
72
+ /* Shadows */
73
+ --shadow-lg: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
74
+ }
75
+
76
+ /* ═══════════════════════════════════════════════════════════════
77
+ Typography (SF Pro / System)
78
+ ═══════════════════════════════════════════════════════════════ */
79
+ * {
80
+ margin: 0;
81
+ padding: 0;
82
+ box-sizing: border-box;
83
+ }
84
+
85
+ body {
86
+ font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
87
+ font-size: 15px;
88
+ line-height: 1.5;
89
+ -webkit-font-smoothing: antialiased;
90
+ -moz-osx-font-smoothing: grayscale;
91
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
92
+ min-height: 100vh;
93
+ display: flex;
94
+ flex-direction: column;
95
+ align-items: center;
96
+ justify-content: center;
97
+ padding: 20px;
98
+ color: var(--text-primary);
99
+ }
100
+
101
+ .mono {
102
+ font-family: "SF Mono", "Menlo", "Monaco", monospace;
103
+ }
104
+
105
+ /* ═══════════════════════════════════════════════════════════════
106
+ Page Header
107
+ ═══════════════════════════════════════════════════════════════ */
108
+ .page-header {
109
+ text-align: center;
110
+ margin-bottom: 24px;
111
+ }
112
+
113
+ .page-header h1 {
114
+ font-size: 28px;
115
+ font-weight: 600;
116
+ margin-bottom: 8px;
117
+ display: flex;
118
+ align-items: center;
119
+ justify-content: center;
120
+ gap: 12px;
121
+ }
122
+
123
+ .page-header p {
124
+ color: var(--text-secondary);
125
+ font-size: 16px;
126
+ }
127
+
128
+ .badge {
129
+ display: inline-flex;
130
+ align-items: center;
131
+ gap: 6px;
132
+ background: rgba(218, 119, 86, 0.2);
133
+ color: var(--claude-orange);
134
+ padding: 4px 10px;
135
+ border-radius: 12px;
136
+ font-size: 12px;
137
+ font-weight: 500;
138
+ }
139
+
140
+ /* ═══════════════════════════════════════════════════════════════
141
+ Scenario Selector
142
+ ═══════════════════════════════════════════════════════════════ */
143
+ .scenario-bar {
144
+ display: flex;
145
+ gap: 12px;
146
+ margin-bottom: 24px;
147
+ flex-wrap: wrap;
148
+ justify-content: center;
149
+ }
150
+
151
+ .scenario-btn {
152
+ display: flex;
153
+ align-items: center;
154
+ gap: 8px;
155
+ padding: 12px 20px;
156
+ background: rgba(255, 255, 255, 0.05);
157
+ border: 1px solid rgba(255, 255, 255, 0.1);
158
+ border-radius: 12px;
159
+ color: var(--text-secondary);
160
+ cursor: pointer;
161
+ transition: all 0.2s ease;
162
+ font-size: 14px;
163
+ font-weight: 500;
164
+ }
165
+
166
+ .scenario-btn:hover {
167
+ background: rgba(255, 255, 255, 0.1);
168
+ border-color: rgba(255, 255, 255, 0.2);
169
+ color: var(--text-primary);
170
+ transform: translateY(-2px);
171
+ }
172
+
173
+ .scenario-btn.active {
174
+ background: rgba(218, 119, 86, 0.2);
175
+ border-color: var(--claude-orange);
176
+ color: var(--claude-orange);
177
+ }
178
+
179
+ .scenario-btn.playing {
180
+ animation: pulse-border 1.5s ease-in-out infinite;
181
+ }
182
+
183
+ @keyframes pulse-border {
184
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(218, 119, 86, 0.4); }
185
+ 50% { box-shadow: 0 0 0 4px rgba(218, 119, 86, 0); }
186
+ }
187
+
188
+ .scenario-btn .icon {
189
+ font-size: 18px;
190
+ }
191
+
192
+ .replay-btn {
193
+ display: flex;
194
+ align-items: center;
195
+ gap: 6px;
196
+ padding: 10px 16px;
197
+ background: rgba(110, 181, 255, 0.1);
198
+ border: 1px solid rgba(110, 181, 255, 0.3);
199
+ border-radius: 12px;
200
+ color: var(--link-color);
201
+ cursor: pointer;
202
+ transition: all 0.2s ease;
203
+ font-size: 13px;
204
+ font-weight: 500;
205
+ }
206
+
207
+ .replay-btn:hover {
208
+ background: rgba(110, 181, 255, 0.2);
209
+ transform: translateY(-2px);
210
+ }
211
+
212
+ .replay-btn:disabled {
213
+ opacity: 0.5;
214
+ cursor: not-allowed;
215
+ transform: none;
216
+ }
217
+
218
+ .controls-bar {
219
+ display: flex;
220
+ gap: 12px;
221
+ margin-bottom: 16px;
222
+ align-items: center;
223
+ }
224
+
225
+ .speed-control {
226
+ display: flex;
227
+ align-items: center;
228
+ gap: 8px;
229
+ color: var(--text-muted);
230
+ font-size: 12px;
231
+ }
232
+
233
+ .speed-control select {
234
+ background: rgba(255, 255, 255, 0.05);
235
+ border: 1px solid rgba(255, 255, 255, 0.1);
236
+ border-radius: 6px;
237
+ color: var(--text-secondary);
238
+ padding: 6px 10px;
239
+ font-size: 12px;
240
+ cursor: pointer;
241
+ }
242
+
243
+ .speed-control select:focus {
244
+ outline: none;
245
+ border-color: var(--link-color);
246
+ }
247
+
248
+ .progress-indicator {
249
+ display: flex;
250
+ align-items: center;
251
+ gap: 8px;
252
+ color: var(--text-muted);
253
+ font-size: 12px;
254
+ }
255
+
256
+ .progress-bar {
257
+ width: 80px;
258
+ height: 4px;
259
+ background: rgba(255, 255, 255, 0.1);
260
+ border-radius: 2px;
261
+ overflow: hidden;
262
+ }
263
+
264
+ .progress-fill {
265
+ height: 100%;
266
+ background: var(--success-color);
267
+ border-radius: 2px;
268
+ transition: width 0.3s ease;
269
+ }
270
+
271
+ .share-btn {
272
+ display: flex;
273
+ align-items: center;
274
+ gap: 6px;
275
+ padding: 8px 14px;
276
+ background: rgba(255, 255, 255, 0.05);
277
+ border: 1px solid rgba(255, 255, 255, 0.1);
278
+ border-radius: 8px;
279
+ color: var(--text-secondary);
280
+ cursor: pointer;
281
+ transition: all 0.2s ease;
282
+ font-size: 12px;
283
+ font-weight: 500;
284
+ }
285
+
286
+ .share-btn:hover {
287
+ background: rgba(255, 255, 255, 0.1);
288
+ color: var(--text-primary);
289
+ }
290
+
291
+ .share-btn.copied {
292
+ background: rgba(40, 200, 64, 0.15);
293
+ border-color: var(--success-color);
294
+ color: var(--success-color);
295
+ }
296
+
297
+ .share-btn .share-icon {
298
+ font-size: 14px;
299
+ }
300
+
301
+ /* ═══════════════════════════════════════════════════════════════
302
+ Claude Desktop Window Frame
303
+ ═══════════════════════════════════════════════════════════════ */
304
+ .claude-window {
305
+ width: 100%;
306
+ max-width: 800px;
307
+ background: var(--window-bg);
308
+ border-radius: 12px;
309
+ box-shadow: var(--shadow-lg);
310
+ overflow: hidden;
311
+ border: 1px solid var(--window-border);
312
+ }
313
+
314
+ .window-chrome {
315
+ height: 52px;
316
+ background: var(--window-chrome);
317
+ display: flex;
318
+ align-items: center;
319
+ padding: 0 16px;
320
+ border-bottom: 1px solid var(--window-border);
321
+ }
322
+
323
+ .traffic-lights {
324
+ display: flex;
325
+ gap: 8px;
326
+ }
327
+
328
+ .traffic-light {
329
+ width: 12px;
330
+ height: 12px;
331
+ border-radius: 50%;
332
+ }
333
+
334
+ .traffic-light.red { background: var(--traffic-red); }
335
+ .traffic-light.yellow { background: var(--traffic-yellow); }
336
+ .traffic-light.green { background: var(--traffic-green); }
337
+
338
+ .window-title {
339
+ flex: 1;
340
+ text-align: center;
341
+ font-size: 13px;
342
+ color: var(--text-secondary);
343
+ font-weight: 500;
344
+ }
345
+
346
+ .window-controls {
347
+ width: 52px;
348
+ }
349
+
350
+ /* ═══════════════════════════════════════════════════════════════
351
+ Chat Container
352
+ ═══════════════════════════════════════════════════════════════ */
353
+ .chat-container {
354
+ height: 520px;
355
+ overflow-y: auto;
356
+ padding: 24px;
357
+ display: flex;
358
+ flex-direction: column;
359
+ gap: 20px;
360
+ scroll-behavior: smooth;
361
+ }
362
+
363
+ /* Loading Skeleton */
364
+ .loading-skeleton {
365
+ display: flex;
366
+ flex-direction: column;
367
+ gap: 16px;
368
+ }
369
+
370
+ .skeleton-message {
371
+ display: flex;
372
+ gap: 12px;
373
+ align-items: flex-start;
374
+ }
375
+
376
+ .skeleton-avatar {
377
+ width: 28px;
378
+ height: 28px;
379
+ border-radius: 50%;
380
+ background: linear-gradient(90deg, var(--window-border) 25%, #4a4a4a 50%, var(--window-border) 75%);
381
+ background-size: 200% 100%;
382
+ animation: skeleton-shimmer 1.5s infinite;
383
+ }
384
+
385
+ .skeleton-lines {
386
+ flex: 1;
387
+ display: flex;
388
+ flex-direction: column;
389
+ gap: 8px;
390
+ }
391
+
392
+ .skeleton-line {
393
+ height: 16px;
394
+ border-radius: 4px;
395
+ background: linear-gradient(90deg, var(--window-border) 25%, #4a4a4a 50%, var(--window-border) 75%);
396
+ background-size: 200% 100%;
397
+ animation: skeleton-shimmer 1.5s infinite;
398
+ }
399
+
400
+ @keyframes skeleton-shimmer {
401
+ 0% { background-position: 200% 0; }
402
+ 100% { background-position: -200% 0; }
403
+ }
404
+
405
+ .chat-container::-webkit-scrollbar {
406
+ width: 8px;
407
+ }
408
+
409
+ .chat-container::-webkit-scrollbar-track {
410
+ background: transparent;
411
+ }
412
+
413
+ .chat-container::-webkit-scrollbar-thumb {
414
+ background: var(--window-border);
415
+ border-radius: 4px;
416
+ }
417
+
418
+ /* ═══════════════════════════════════════════════════════════════
419
+ Message Bubbles
420
+ ═══════════════════════════════════════════════════════════════ */
421
+ .message {
422
+ max-width: 100%;
423
+ animation: message-appear 0.3s ease-out;
424
+ }
425
+
426
+ @keyframes message-appear {
427
+ from {
428
+ opacity: 0;
429
+ transform: translateY(10px);
430
+ }
431
+ to {
432
+ opacity: 1;
433
+ transform: translateY(0);
434
+ }
435
+ }
436
+
437
+ .message-header {
438
+ display: flex;
439
+ align-items: center;
440
+ gap: 10px;
441
+ margin-bottom: 8px;
442
+ }
443
+
444
+ .message-avatar {
445
+ width: 28px;
446
+ height: 28px;
447
+ border-radius: 50%;
448
+ display: flex;
449
+ align-items: center;
450
+ justify-content: center;
451
+ font-size: 14px;
452
+ }
453
+
454
+ .message.user .message-avatar {
455
+ background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
456
+ color: white;
457
+ font-weight: 600;
458
+ font-size: 12px;
459
+ }
460
+
461
+ .message.claude .message-avatar {
462
+ background: linear-gradient(135deg, var(--claude-orange) 0%, #c56644 100%);
463
+ color: white;
464
+ font-weight: 600;
465
+ font-size: 12px;
466
+ }
467
+
468
+ .message.claude .message-avatar svg {
469
+ width: 16px;
470
+ height: 16px;
471
+ }
472
+
473
+ .message-sender {
474
+ font-weight: 600;
475
+ font-size: 14px;
476
+ }
477
+
478
+ .message.user .message-sender { color: #a5b4fc; }
479
+ .message.claude .message-sender { color: var(--claude-orange); }
480
+
481
+ .message-time {
482
+ color: var(--text-muted);
483
+ font-size: 12px;
484
+ }
485
+
486
+ .message-content {
487
+ padding: 16px;
488
+ border-radius: 12px;
489
+ font-size: 15px;
490
+ line-height: 1.6;
491
+ }
492
+
493
+ .message.user .message-content {
494
+ background: var(--user-bubble-bg);
495
+ }
496
+
497
+ .message.claude .message-content {
498
+ background: var(--claude-bubble-bg);
499
+ }
500
+
501
+ /* ═══════════════════════════════════════════════════════════════
502
+ Tool Call Box
503
+ ═══════════════════════════════════════════════════════════════ */
504
+ .tool-call {
505
+ background: var(--tool-box-bg);
506
+ border: 1px solid var(--tool-box-border);
507
+ border-radius: 8px;
508
+ margin: 12px 0;
509
+ overflow: hidden;
510
+ }
511
+
512
+ .tool-header {
513
+ display: flex;
514
+ align-items: center;
515
+ gap: 10px;
516
+ padding: 12px 16px;
517
+ background: var(--tool-header-bg);
518
+ cursor: pointer;
519
+ transition: background 0.2s;
520
+ }
521
+
522
+ .tool-header:hover {
523
+ background: #2a2a2a;
524
+ }
525
+
526
+ .tool-icon {
527
+ font-size: 16px;
528
+ }
529
+
530
+ .tool-name {
531
+ font-family: "SF Mono", monospace;
532
+ font-size: 13px;
533
+ color: var(--link-color);
534
+ font-weight: 500;
535
+ }
536
+
537
+ .tool-status {
538
+ margin-left: auto;
539
+ display: inline-flex;
540
+ align-items: center;
541
+ gap: 6px;
542
+ font-size: 12px;
543
+ color: var(--text-muted);
544
+ }
545
+
546
+ .tool-status.running {
547
+ color: var(--warning-color);
548
+ }
549
+
550
+ .tool-status.running::before {
551
+ content: '';
552
+ display: inline-block;
553
+ width: 12px;
554
+ height: 12px;
555
+ border: 2px solid var(--warning-color);
556
+ border-top-color: transparent;
557
+ border-radius: 50%;
558
+ animation: spin 0.8s linear infinite;
559
+ margin-right: 6px;
560
+ }
561
+
562
+ @keyframes spin {
563
+ to { transform: rotate(360deg); }
564
+ }
565
+
566
+ .tool-status.success {
567
+ color: var(--success-color);
568
+ }
569
+
570
+ .tool-chevron {
571
+ color: var(--text-muted);
572
+ transition: transform 0.3s ease;
573
+ font-size: 10px;
574
+ }
575
+
576
+ .tool-call.expanded .tool-chevron {
577
+ transform: rotate(180deg);
578
+ }
579
+
580
+ .tool-body {
581
+ max-height: 0;
582
+ overflow: hidden;
583
+ transition: max-height 0.3s ease-out;
584
+ }
585
+
586
+ .tool-call.expanded .tool-body {
587
+ max-height: 400px;
588
+ }
589
+
590
+ .tool-section {
591
+ padding: 12px 16px;
592
+ border-top: 1px solid var(--tool-box-border);
593
+ }
594
+
595
+ .tool-section-label {
596
+ font-size: 11px;
597
+ text-transform: uppercase;
598
+ letter-spacing: 0.5px;
599
+ color: var(--text-muted);
600
+ margin-bottom: 8px;
601
+ }
602
+
603
+ .tool-params {
604
+ font-family: "SF Mono", monospace;
605
+ font-size: 13px;
606
+ color: var(--code-text);
607
+ background: var(--code-bg);
608
+ padding: 12px;
609
+ border-radius: 6px;
610
+ white-space: pre-wrap;
611
+ }
612
+
613
+ .tool-result {
614
+ font-size: 13px;
615
+ color: var(--text-secondary);
616
+ }
617
+
618
+ .tool-result .result-item {
619
+ padding: 8px 12px;
620
+ margin: 0 -12px;
621
+ border-bottom: 1px solid var(--tool-box-border);
622
+ border-left: 2px solid transparent;
623
+ border-radius: 4px;
624
+ transition: all 0.15s ease;
625
+ cursor: default;
626
+ }
627
+
628
+ .tool-result .result-item:hover {
629
+ background: rgba(255, 255, 255, 0.03);
630
+ border-left-color: var(--claude-orange);
631
+ }
632
+
633
+ .tool-result .result-item:last-child {
634
+ border-bottom: none;
635
+ }
636
+
637
+ .result-channel {
638
+ color: var(--link-color);
639
+ font-weight: 500;
640
+ }
641
+
642
+ .result-user {
643
+ color: #f0abfc;
644
+ font-weight: 500;
645
+ }
646
+
647
+ .result-time {
648
+ color: var(--text-muted);
649
+ font-size: 12px;
650
+ }
651
+
652
+ /* ═══════════════════════════════════════════════════════════════
653
+ Typing Indicator
654
+ ═══════════════════════════════════════════════════════════════ */
655
+ .typing-indicator {
656
+ display: flex;
657
+ align-items: center;
658
+ gap: 8px;
659
+ padding: 12px 16px;
660
+ background: var(--claude-bubble-bg);
661
+ border-radius: 12px;
662
+ width: fit-content;
663
+ }
664
+
665
+ .typing-dots {
666
+ display: flex;
667
+ gap: 4px;
668
+ }
669
+
670
+ .typing-dot {
671
+ width: 8px;
672
+ height: 8px;
673
+ background: var(--text-muted);
674
+ border-radius: 50%;
675
+ animation: typing-bounce 1.4s ease-in-out infinite;
676
+ }
677
+
678
+ .typing-dot:nth-child(2) { animation-delay: 0.16s; }
679
+ .typing-dot:nth-child(3) { animation-delay: 0.32s; }
680
+
681
+ @keyframes typing-bounce {
682
+ 0%, 80%, 100% { transform: translateY(0); opacity: 0.4; }
683
+ 40% { transform: translateY(-4px); opacity: 1; }
684
+ }
685
+
686
+ .typing-cursor {
687
+ display: inline-block;
688
+ width: 2px;
689
+ height: 1em;
690
+ background: var(--claude-orange);
691
+ margin-left: 1px;
692
+ animation: cursor-blink 0.8s ease-in-out infinite;
693
+ vertical-align: text-bottom;
694
+ }
695
+
696
+ @keyframes cursor-blink {
697
+ 0%, 50% { opacity: 1; }
698
+ 51%, 100% { opacity: 0; }
699
+ }
700
+
701
+ /* ═══════════════════════════════════════════════════════════════
702
+ Input Bar
703
+ ═══════════════════════════════════════════════════════════════ */
704
+ .input-bar {
705
+ display: flex;
706
+ align-items: center;
707
+ padding: 16px 20px;
708
+ background: var(--window-chrome);
709
+ border-top: 1px solid var(--window-border);
710
+ gap: 12px;
711
+ }
712
+
713
+ .input-field {
714
+ flex: 1;
715
+ background: var(--window-bg);
716
+ border: 1px solid var(--window-border);
717
+ border-radius: 24px;
718
+ padding: 12px 20px;
719
+ color: var(--text-secondary);
720
+ font-size: 14px;
721
+ }
722
+
723
+ .tools-button {
724
+ display: flex;
725
+ align-items: center;
726
+ gap: 6px;
727
+ padding: 10px 16px;
728
+ background: rgba(218, 119, 86, 0.15);
729
+ border: 1px solid rgba(218, 119, 86, 0.3);
730
+ border-radius: 20px;
731
+ color: var(--claude-orange);
732
+ cursor: pointer;
733
+ transition: all 0.2s;
734
+ font-size: 13px;
735
+ font-weight: 500;
736
+ position: relative;
737
+ }
738
+
739
+ .tools-button:hover {
740
+ background: rgba(218, 119, 86, 0.25);
741
+ }
742
+
743
+ .tools-button .icon {
744
+ font-size: 16px;
745
+ }
746
+
747
+ /* ═══════════════════════════════════════════════════════════════
748
+ Tools Dropdown
749
+ ═══════════════════════════════════════════════════════════════ */
750
+ .tools-dropdown {
751
+ position: absolute;
752
+ bottom: calc(100% + 8px);
753
+ right: 0;
754
+ width: 320px;
755
+ background: var(--window-bg);
756
+ border: 1px solid var(--window-border);
757
+ border-radius: 12px;
758
+ box-shadow: var(--shadow-lg);
759
+ opacity: 0;
760
+ visibility: hidden;
761
+ transform: translateY(10px);
762
+ transition: all 0.2s ease;
763
+ z-index: 100;
764
+ }
765
+
766
+ .tools-button:hover .tools-dropdown,
767
+ .tools-dropdown:hover {
768
+ opacity: 1;
769
+ visibility: visible;
770
+ transform: translateY(0);
771
+ }
772
+
773
+ .dropdown-header {
774
+ padding: 12px 16px;
775
+ border-bottom: 1px solid var(--window-border);
776
+ font-weight: 600;
777
+ font-size: 14px;
778
+ display: flex;
779
+ align-items: center;
780
+ gap: 8px;
781
+ }
782
+
783
+ .dropdown-list {
784
+ max-height: 300px;
785
+ overflow-y: auto;
786
+ }
787
+
788
+ .dropdown-item {
789
+ padding: 10px 16px;
790
+ border-bottom: 1px solid var(--tool-box-border);
791
+ cursor: default;
792
+ }
793
+
794
+ .dropdown-item:last-child {
795
+ border-bottom: none;
796
+ }
797
+
798
+ .dropdown-item:hover {
799
+ background: rgba(255, 255, 255, 0.03);
800
+ }
801
+
802
+ .dropdown-item-name {
803
+ font-family: "SF Mono", monospace;
804
+ font-size: 13px;
805
+ color: var(--link-color);
806
+ margin-bottom: 2px;
807
+ }
808
+
809
+ .dropdown-item-desc {
810
+ font-size: 12px;
811
+ color: var(--text-muted);
812
+ }
813
+
814
+ /* ═══════════════════════════════════════════════════════════════
815
+ Code Inline
816
+ ═══════════════════════════════════════════════════════════════ */
817
+ code {
818
+ font-family: "SF Mono", monospace;
819
+ background: var(--code-bg);
820
+ padding: 2px 6px;
821
+ border-radius: 4px;
822
+ font-size: 13px;
823
+ }
824
+
825
+ strong {
826
+ font-weight: 600;
827
+ }
828
+
829
+ em {
830
+ font-style: italic;
831
+ color: var(--text-secondary);
832
+ }
833
+
834
+ /* ═══════════════════════════════════════════════════════════════
835
+ Footer
836
+ ═══════════════════════════════════════════════════════════════ */
837
+ .page-footer {
838
+ margin-top: 24px;
839
+ text-align: center;
840
+ color: var(--text-muted);
841
+ font-size: 13px;
842
+ }
843
+
844
+ .page-footer a {
845
+ color: var(--link-color);
846
+ text-decoration: none;
847
+ }
848
+
849
+ .page-footer a:hover {
850
+ text-decoration: underline;
851
+ }
852
+
853
+ kbd {
854
+ display: inline-block;
855
+ padding: 2px 6px;
856
+ font-family: "SF Mono", monospace;
857
+ font-size: 10px;
858
+ background: rgba(255, 255, 255, 0.1);
859
+ border: 1px solid rgba(255, 255, 255, 0.2);
860
+ border-radius: 4px;
861
+ color: var(--text-secondary);
862
+ }
863
+
864
+ /* Fullscreen / Presentation Mode */
865
+ body.fullscreen-mode {
866
+ padding: 0;
867
+ justify-content: center;
868
+ }
869
+
870
+ body.fullscreen-mode .page-header,
871
+ body.fullscreen-mode .scenario-bar,
872
+ body.fullscreen-mode .controls-bar,
873
+ body.fullscreen-mode .page-footer {
874
+ display: none !important;
875
+ }
876
+
877
+ body.fullscreen-mode .claude-window {
878
+ max-width: 100%;
879
+ height: 100vh;
880
+ border-radius: 0;
881
+ border: none;
882
+ }
883
+
884
+ body.fullscreen-mode .chat-container {
885
+ height: calc(100vh - 52px - 68px);
886
+ }
887
+
888
+ .fullscreen-hint {
889
+ position: fixed;
890
+ bottom: 20px;
891
+ right: 20px;
892
+ background: rgba(0, 0, 0, 0.8);
893
+ color: var(--text-secondary);
894
+ padding: 8px 12px;
895
+ border-radius: 6px;
896
+ font-size: 12px;
897
+ opacity: 0;
898
+ transition: opacity 0.3s;
899
+ pointer-events: none;
900
+ }
901
+
902
+ body.fullscreen-mode .fullscreen-hint {
903
+ opacity: 1;
904
+ }
905
+
906
+ /* ═══════════════════════════════════════════════════════════════
907
+ Accessibility
908
+ ═══════════════════════════════════════════════════════════════ */
909
+ @media (prefers-reduced-motion: reduce) {
910
+ *, *::before, *::after {
911
+ animation-duration: 0.01ms !important;
912
+ animation-iteration-count: 1 !important;
913
+ transition-duration: 0.01ms !important;
914
+ }
915
+ }
916
+
917
+ .scenario-btn:focus-visible,
918
+ .replay-btn:focus-visible,
919
+ .tools-button:focus-visible,
920
+ .speed-control select:focus-visible {
921
+ outline: 2px solid var(--link-color);
922
+ outline-offset: 2px;
923
+ }
924
+
925
+ .tool-header:focus-visible {
926
+ outline: 2px solid var(--link-color);
927
+ outline-offset: -2px;
928
+ }
929
+
930
+ /* ═══════════════════════════════════════════════════════════════
931
+ Responsive
932
+ ═══════════════════════════════════════════════════════════════ */
933
+ @media (max-width: 600px) {
934
+ body {
935
+ padding: 12px;
936
+ }
937
+
938
+ .page-header h1 {
939
+ font-size: 22px;
940
+ }
941
+
942
+ .scenario-bar {
943
+ gap: 8px;
944
+ }
945
+
946
+ .scenario-btn {
947
+ padding: 10px 14px;
948
+ font-size: 13px;
949
+ }
950
+
951
+ .scenario-btn .label {
952
+ display: none;
953
+ }
954
+
955
+ .chat-container {
956
+ height: 450px;
957
+ padding: 16px;
958
+ }
959
+
960
+ .tools-dropdown {
961
+ width: 280px;
962
+ right: auto;
963
+ left: 50%;
964
+ transform: translateX(-50%) translateY(0);
965
+ }
966
+
967
+ .tools-button:hover .tools-dropdown,
968
+ .tools-dropdown:hover {
969
+ transform: translateX(-50%) translateY(0);
970
+ }
971
+ }
972
+
973
+ /* ═══════════════════════════════════════════════════════════════
974
+ Production Polish - Title, Captions, Transitions, Closing
975
+ ═══════════════════════════════════════════════════════════════ */
976
+
977
+ /* Title Card */
978
+ .title-card {
979
+ position: absolute;
980
+ inset: 0;
981
+ top: 32px; /* Below window chrome */
982
+ display: none;
983
+ flex-direction: column;
984
+ align-items: center;
985
+ justify-content: center;
986
+ background: var(--window-bg);
987
+ z-index: 100;
988
+ opacity: 0;
989
+ transition: opacity 0.5s ease;
990
+ }
991
+
992
+ .title-card.visible {
993
+ display: flex;
994
+ opacity: 1;
995
+ }
996
+
997
+ .title-card h1 {
998
+ color: var(--text-warm);
999
+ letter-spacing: 0.08em;
1000
+ font-weight: 300;
1001
+ font-size: 28px;
1002
+ margin: 16px 0 8px;
1003
+ }
1004
+
1005
+ .title-card .title-logo { font-size: 48px; }
1006
+ .title-card .title-tagline { color: var(--text-secondary); font-size: 16px; }
1007
+ .title-card .title-version { color: var(--text-muted); font-size: 13px; margin-top: 24px; }
1008
+
1009
+ /* Scenario Caption Overlay */
1010
+ .scenario-caption {
1011
+ position: absolute;
1012
+ top: 72px;
1013
+ left: 50%;
1014
+ transform: translateX(-50%);
1015
+ background: rgba(0, 0, 0, 0.75);
1016
+ color: var(--text-primary);
1017
+ padding: 8px 20px;
1018
+ border-radius: 20px;
1019
+ font-size: 13px;
1020
+ font-weight: 500;
1021
+ opacity: 0;
1022
+ transition: opacity 0.3s ease;
1023
+ z-index: 50;
1024
+ pointer-events: none;
1025
+ }
1026
+
1027
+ .scenario-caption.visible {
1028
+ opacity: 1;
1029
+ }
1030
+
1031
+ /* Smooth Transitions */
1032
+ .chat-container {
1033
+ transition: opacity 0.3s ease;
1034
+ }
1035
+
1036
+ .chat-container.fading {
1037
+ opacity: 0;
1038
+ }
1039
+
1040
+ /* Closing Card */
1041
+ .closing-card {
1042
+ position: absolute;
1043
+ inset: 0;
1044
+ top: 32px;
1045
+ display: none;
1046
+ flex-direction: column;
1047
+ align-items: center;
1048
+ justify-content: center;
1049
+ background: var(--window-bg);
1050
+ z-index: 100;
1051
+ opacity: 0;
1052
+ transition: opacity 0.5s ease;
1053
+ }
1054
+
1055
+ .closing-card.visible {
1056
+ display: flex;
1057
+ opacity: 1;
1058
+ }
1059
+
1060
+ .closing-check { font-size: 48px; margin-bottom: 16px; }
1061
+
1062
+ .closing-card h2 {
1063
+ color: var(--text-warm);
1064
+ font-weight: 400;
1065
+ font-size: 24px;
1066
+ margin-bottom: 8px;
1067
+ }
1068
+
1069
+ .closing-cta {
1070
+ color: var(--text-secondary);
1071
+ margin: 8px 0 24px;
1072
+ font-size: 15px;
1073
+ }
1074
+
1075
+ .closing-links code {
1076
+ background: var(--code-bg);
1077
+ color: var(--code-text);
1078
+ padding: 12px 24px;
1079
+ border-radius: 8px;
1080
+ font-size: 14px;
1081
+ font-family: "SF Mono", "Menlo", monospace;
1082
+ }
1083
+
1084
+ .closing-github {
1085
+ margin-top: 24px;
1086
+ color: var(--link-color);
1087
+ font-size: 14px;
1088
+ }
1089
+
1090
+ /* ê Easter Egg - The Rêvasser Wink */
1091
+ .easter-egg {
1092
+ position: absolute;
1093
+ bottom: 16px;
1094
+ right: 20px;
1095
+ color: var(--text-warm);
1096
+ opacity: 0.15;
1097
+ font-size: 14px;
1098
+ font-weight: 300;
1099
+ font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
1100
+ }
1101
+
1102
+ .closing-card.visible .easter-egg {
1103
+ animation: egg-wink 8s ease 2s forwards;
1104
+ }
1105
+
1106
+ @keyframes egg-wink {
1107
+ 0%, 100% { opacity: 0.15; }
1108
+ 10% { opacity: 0.35; }
1109
+ 20% { opacity: 0.15; }
1110
+ }
1111
+ </style>
1112
+ </head>
1113
+ <body>
1114
+ <header class="page-header">
1115
+ <h1>
1116
+ <span>Slack MCP Server</span>
1117
+ <span class="badge">🔧 MCP Demo v1.2</span>
1118
+ </h1>
1119
+ <p>See how Claude uses MCP tools to access your Slack workspace</p>
1120
+ </header>
1121
+
1122
+ <div class="scenario-bar" role="tablist" aria-label="Demo scenarios">
1123
+ <button class="scenario-btn active" data-scenario="search" onclick="runScenario('search')" role="tab" aria-selected="true" aria-label="Search DMs scenario">
1124
+ <span class="icon" aria-hidden="true">🔍</span>
1125
+ <span class="label">Search DMs</span>
1126
+ </button>
1127
+ <button class="scenario-btn" data-scenario="thread" onclick="runScenario('thread')" role="tab" aria-selected="false" aria-label="Get Thread scenario">
1128
+ <span class="icon" aria-hidden="true">📜</span>
1129
+ <span class="label">Get Thread</span>
1130
+ </button>
1131
+ <button class="scenario-btn" data-scenario="list" onclick="runScenario('list')" role="tab" aria-selected="false" aria-label="List DMs scenario">
1132
+ <span class="icon" aria-hidden="true">💬</span>
1133
+ <span class="label">List DMs</span>
1134
+ </button>
1135
+ <button class="scenario-btn" data-scenario="send" onclick="runScenario('send')" role="tab" aria-selected="false" aria-label="Send Message scenario">
1136
+ <span class="icon" aria-hidden="true">✉️</span>
1137
+ <span class="label">Send Message</span>
1138
+ </button>
1139
+ <button class="scenario-btn" data-scenario="multi" onclick="runScenario('multi')" role="tab" aria-selected="false" aria-label="Multi-Tool scenario">
1140
+ <span class="icon" aria-hidden="true">⚡</span>
1141
+ <span class="label">Multi-Tool</span>
1142
+ </button>
1143
+ </div>
1144
+
1145
+ <div class="controls-bar" role="toolbar" aria-label="Demo controls">
1146
+ <button class="replay-btn" id="replayBtn" onclick="replayScenario()" aria-label="Replay current scenario">
1147
+ <span aria-hidden="true">↻</span>
1148
+ <span>Replay</span>
1149
+ </button>
1150
+ <button class="replay-btn" id="autoPlayBtn" onclick="autoPlayAll()" style="background: rgba(40, 200, 64, 0.1); border-color: rgba(40, 200, 64, 0.3); color: var(--success-color);" aria-label="Auto-play all scenarios">
1151
+ <span aria-hidden="true">▶</span>
1152
+ <span>Auto-Play All</span>
1153
+ </button>
1154
+ <div class="speed-control">
1155
+ <label>Speed:</label>
1156
+ <select id="speedSelect" onchange="updateSpeed(this.value)">
1157
+ <option value="0.5">0.5x (Slow - Video)</option>
1158
+ <option value="1" selected>1x (Normal)</option>
1159
+ <option value="1.5">1.5x (Fast)</option>
1160
+ <option value="2">2x (Fastest)</option>
1161
+ </select>
1162
+ </div>
1163
+ <div class="progress-indicator" id="progressIndicator" style="display: none;">
1164
+ <span class="progress-text"></span>
1165
+ <div class="progress-bar"><div class="progress-fill"></div></div>
1166
+ </div>
1167
+ <button class="share-btn" onclick="copyShareLink()" aria-label="Copy link to share">
1168
+ <span class="share-icon">🔗</span>
1169
+ <span class="share-text">Share</span>
1170
+ </button>
1171
+ </div>
1172
+
1173
+ <div class="claude-window">
1174
+ <div class="window-chrome">
1175
+ <div class="traffic-lights">
1176
+ <div class="traffic-light red"></div>
1177
+ <div class="traffic-light yellow"></div>
1178
+ <div class="traffic-light green"></div>
1179
+ </div>
1180
+ <div class="window-title">Claude</div>
1181
+ <div class="window-controls"></div>
1182
+ </div>
1183
+
1184
+ <!-- Title Card (auto-play only) -->
1185
+ <div class="title-card" id="titleCard">
1186
+ <div class="title-logo">💬</div>
1187
+ <h1>Slack MCP Server</h1>
1188
+ <p class="title-tagline">Full Slack access for Claude Desktop</p>
1189
+ <p class="title-version">v1.2 • @jtalk22</p>
1190
+ </div>
1191
+
1192
+ <!-- Scenario Caption Overlay -->
1193
+ <div class="scenario-caption" id="scenarioCaption"></div>
1194
+
1195
+ <!-- Closing Card (auto-play only) -->
1196
+ <div class="closing-card" id="closingCard">
1197
+ <div class="closing-check">✅</div>
1198
+ <h2>Demo Complete</h2>
1199
+ <p class="closing-cta">Full Slack access. No OAuth. No admin approval.</p>
1200
+ <div class="closing-links">
1201
+ <code>npm install -g @jtalk22/slack-mcp</code>
1202
+ </div>
1203
+ <p class="closing-github">github.com/jtalk22/slack-mcp-server</p>
1204
+ <span class="easter-egg">ê</span>
1205
+ </div>
1206
+
1207
+ <div class="chat-container" id="chatContainer" role="log" aria-label="Chat demonstration" aria-live="polite">
1208
+ <!-- Loading skeleton shown before first scenario -->
1209
+ <div class="loading-skeleton" id="loadingSkeleton">
1210
+ <div class="skeleton-message">
1211
+ <div class="skeleton-avatar"></div>
1212
+ <div class="skeleton-lines">
1213
+ <div class="skeleton-line" style="width: 60%"></div>
1214
+ <div class="skeleton-line" style="width: 80%"></div>
1215
+ </div>
1216
+ </div>
1217
+ </div>
1218
+ </div>
1219
+
1220
+ <div class="input-bar">
1221
+ <div class="input-field">Message Claude...</div>
1222
+ <div class="tools-button">
1223
+ <span class="icon">🔨</span>
1224
+ <span>11 tools</span>
1225
+ <div class="tools-dropdown">
1226
+ <div class="dropdown-header">
1227
+ <span>🔨</span> Available Slack Tools
1228
+ </div>
1229
+ <div class="dropdown-list">
1230
+ <div class="dropdown-item">
1231
+ <div class="dropdown-item-name">slack_search_messages</div>
1232
+ <div class="dropdown-item-desc">Search across your workspace</div>
1233
+ </div>
1234
+ <div class="dropdown-item">
1235
+ <div class="dropdown-item-name">slack_list_conversations</div>
1236
+ <div class="dropdown-item-desc">List DMs and channels</div>
1237
+ </div>
1238
+ <div class="dropdown-item">
1239
+ <div class="dropdown-item-name">slack_conversations_history</div>
1240
+ <div class="dropdown-item-desc">Get messages from a conversation</div>
1241
+ </div>
1242
+ <div class="dropdown-item">
1243
+ <div class="dropdown-item-name">slack_get_thread</div>
1244
+ <div class="dropdown-item-desc">Get all replies in a thread</div>
1245
+ </div>
1246
+ <div class="dropdown-item">
1247
+ <div class="dropdown-item-name">slack_send_message</div>
1248
+ <div class="dropdown-item-desc">Send a message to any channel</div>
1249
+ </div>
1250
+ <div class="dropdown-item">
1251
+ <div class="dropdown-item-name">slack_get_full_conversation</div>
1252
+ <div class="dropdown-item-desc">Export full history with threads</div>
1253
+ </div>
1254
+ <div class="dropdown-item">
1255
+ <div class="dropdown-item-name">slack_users_info</div>
1256
+ <div class="dropdown-item-desc">Get user details</div>
1257
+ </div>
1258
+ <div class="dropdown-item">
1259
+ <div class="dropdown-item-name">slack_list_users</div>
1260
+ <div class="dropdown-item-desc">List workspace users</div>
1261
+ </div>
1262
+ <div class="dropdown-item">
1263
+ <div class="dropdown-item-name">slack_health_check</div>
1264
+ <div class="dropdown-item-desc">Verify token validity</div>
1265
+ </div>
1266
+ <div class="dropdown-item">
1267
+ <div class="dropdown-item-name">slack_token_status</div>
1268
+ <div class="dropdown-item-desc">Check token health</div>
1269
+ </div>
1270
+ <div class="dropdown-item">
1271
+ <div class="dropdown-item-name">slack_refresh_tokens</div>
1272
+ <div class="dropdown-item-desc">Auto-extract fresh tokens</div>
1273
+ </div>
1274
+ </div>
1275
+ </div>
1276
+ </div>
1277
+ </div>
1278
+ </div>
1279
+
1280
+ <div class="fullscreen-hint">Press <kbd>F</kbd> or <kbd>Esc</kbd> to exit</div>
1281
+
1282
+ <footer class="page-footer">
1283
+ <p>
1284
+ Made by <a href="https://github.com/jtalk22" target="_blank">@jtalk22</a> ·
1285
+ <a href="https://github.com/jtalk22/slack-mcp-server" target="_blank">GitHub</a> ·
1286
+ <a href="https://www.npmjs.com/package/@jtalk22/slack-mcp" target="_blank">npm</a> ·
1287
+ <a href="demo.html">Web UI Demo</a>
1288
+ </p>
1289
+ <p style="margin-top: 8px; font-size: 11px; color: var(--text-muted);">
1290
+ Keyboard: <kbd>1-5</kbd> scenarios · <kbd>R</kbd> replay · <kbd>A</kbd> auto-play · <kbd>F</kbd> fullscreen · <kbd>Esc</kbd> exit
1291
+ </p>
1292
+ <p style="margin-top: 12px; font-size: 11px; color: var(--text-muted);">
1293
+ © 2026 James Lambert · MIT License
1294
+ </p>
1295
+ </footer>
1296
+
1297
+ <script>
1298
+ // ═══════════════════════════════════════════════════════════════
1299
+ // Scenario Data
1300
+ // ═══════════════════════════════════════════════════════════════
1301
+ const scenarios = {
1302
+ search: {
1303
+ userMessage: "Find all messages about the API key from last week",
1304
+ claudeIntro: "I'll search your Slack workspace for messages about API keys from the past week.",
1305
+ toolCall: {
1306
+ name: "slack_search_messages",
1307
+ params: {
1308
+ query: "API key",
1309
+ count: 20
1310
+ },
1311
+ result: [
1312
+ { channel: "#engineering", user: "Sarah Chen", time: "Jan 15, 2:34 PM", text: "Here's the staging API key: <code>sk-abc123-staging-xyz</code>" },
1313
+ { channel: "DM with Mike", user: "Mike Johnson", time: "Jan 12, 10:15 AM", text: "Don't forget to rotate the API key before the deploy" },
1314
+ { channel: "#alerts", user: "DevOps Bot", time: "Jan 10, 9:00 AM", text: "API key rotation reminder: 3 days until expiry" }
1315
+ ]
1316
+ },
1317
+ claudeSummary: "I found <strong>3 messages</strong> mentioning API keys from last week:\n\n1. <strong>Sarah Chen</strong> shared the staging API key in #engineering on Jan 15th\n2. <strong>Mike Johnson</strong> reminded you about key rotation in a DM on Jan 12th\n3. <strong>DevOps Bot</strong> posted an expiry reminder in #alerts on Jan 10th\n\nWould you like me to get more details on any of these?"
1318
+ },
1319
+
1320
+ thread: {
1321
+ userMessage: "What was decided in the Q4 launch thread?",
1322
+ claudeIntro: "Let me find that thread and get the full context of the discussion.",
1323
+ toolCall: {
1324
+ name: "slack_get_thread",
1325
+ params: {
1326
+ channel: "C04N8JXKZ7M",
1327
+ thread_ts: "1704729600.847293"
1328
+ },
1329
+ result: [
1330
+ { user: "Alex Kim", time: "Jan 8, 3:00 PM", text: "Kicking off Q4 launch planning. Key dates?" },
1331
+ { user: "Sarah Chen", time: "Jan 8, 3:15 PM", text: "Marketing wants the 15th for press embargo lift" },
1332
+ { user: "Mike Johnson", time: "Jan 8, 3:22 PM", text: "Engineering can hit the 12th for feature freeze" },
1333
+ { user: "Jordan Lee", time: "Jan 8, 4:00 PM", text: "Let's lock in: Feature freeze Jan 12, Launch Jan 18" },
1334
+ { user: "Alex Kim", time: "Jan 8, 4:05 PM", text: "✅ Approved. I'll update the roadmap" }
1335
+ ]
1336
+ },
1337
+ claudeSummary: "The Q4 launch thread concluded with these <strong>key decisions</strong>:\n\n• <strong>Feature freeze:</strong> January 12th\n• <strong>Press embargo lift:</strong> January 15th \n• <strong>Launch date:</strong> January 18th\n\n<strong>Jordan Lee</strong> proposed the final timeline and <strong>Alex Kim</strong> approved it. Would you like me to pull any related threads or documents?"
1338
+ },
1339
+
1340
+ list: {
1341
+ userMessage: "Who has messaged me recently?",
1342
+ claudeIntro: "I'll check your recent direct messages to see who's reached out.",
1343
+ toolCall: {
1344
+ name: "slack_list_conversations",
1345
+ params: {
1346
+ types: "im",
1347
+ limit: 10,
1348
+ discover_dms: true
1349
+ },
1350
+ result: [
1351
+ { name: "Sarah Chen", id: "D04AXQK8N2P", lastMessage: "2 hours ago", preview: "Thanks for the review!" },
1352
+ { name: "Mike Johnson", id: "D04BYR7M3Q1", lastMessage: "Yesterday", preview: "Can we sync on the API changes?" },
1353
+ { name: "Alex Kim", id: "D04CZS6L4R2", lastMessage: "2 days ago", preview: "Q4 roadmap is updated" },
1354
+ { name: "Jordan Lee", id: "D04DWT5K5S3", lastMessage: "3 days ago", preview: "Meeting notes attached" },
1355
+ { name: "DevOps Bot", id: "D04EXU4J6T4", lastMessage: "4 days ago", preview: "Deploy completed successfully" }
1356
+ ]
1357
+ },
1358
+ claudeSummary: "You have recent DMs from <strong>5 people</strong>:\n\n• <strong>Sarah Chen</strong> — 2 hours ago: \"Thanks for the review!\"\n• <strong>Mike Johnson</strong> — Yesterday: Asking about API changes sync\n• <strong>Alex Kim</strong> — 2 days ago: Q4 roadmap update\n• <strong>Jordan Lee</strong> — 3 days ago: Meeting notes\n• <strong>DevOps Bot</strong> — 4 days ago: Deploy notification\n\nWant me to pull the full conversation with any of them?"
1359
+ },
1360
+
1361
+ send: {
1362
+ userMessage: "Send a quick update to #general that the deploy is complete",
1363
+ claudeIntro: "I'll post that update to #general for you.",
1364
+ toolCall: {
1365
+ name: "slack_send_message",
1366
+ params: {
1367
+ channel_id: "C04GENERAL1",
1368
+ text: "✅ Deploy complete! All services are healthy and running on v2.4.1."
1369
+ },
1370
+ result: {
1371
+ success: true,
1372
+ channel: "#general",
1373
+ timestamp: "1705612847.293847",
1374
+ permalink: "https://acme-corp.slack.com/archives/C04GENERAL1/p1705612847293847"
1375
+ }
1376
+ },
1377
+ claudeSummary: "Done! I've posted your update to <strong>#general</strong>:\n\n<em>\"✅ Deploy complete! All services are healthy and running on v2.4.1.\"</em>\n\nThe message was sent at <strong>2:34 PM</strong>. Would you like me to add any follow-up or tag specific people?"
1378
+ },
1379
+
1380
+ multi: {
1381
+ userMessage: "Find the latest deploy thread and summarize it to #updates",
1382
+ claudeIntro: "I'll search for the deploy thread, then post a summary to #updates.",
1383
+ isMultiTool: true,
1384
+ toolCalls: [
1385
+ {
1386
+ name: "slack_search_messages",
1387
+ params: {
1388
+ query: "deploy thread",
1389
+ count: 5
1390
+ },
1391
+ result: [
1392
+ { channel: "#engineering", user: "DevOps Bot", time: "Today, 11:42 AM", text: "🚀 Deploy v2.4.1 starting..." },
1393
+ { channel: "#engineering", user: "Sarah Chen", time: "Today, 11:45 AM", text: "All tests passing, proceeding with rollout" }
1394
+ ]
1395
+ },
1396
+ {
1397
+ name: "slack_send_message",
1398
+ params: {
1399
+ channel_id: "C04UPDATES2K",
1400
+ text: "📋 Deploy Summary (v2.4.1):\n• Started: 11:42 AM\n• Status: Complete\n• Tests: All passing"
1401
+ },
1402
+ result: {
1403
+ success: true,
1404
+ channel: "#updates",
1405
+ timestamp: "1705614000.123456"
1406
+ }
1407
+ }
1408
+ ],
1409
+ claudeSummary: "Done! I found the deploy thread from this morning and posted a summary to <strong>#updates</strong>:\n\n<em>\"📋 Deploy Summary (v2.4.1): Started 11:42 AM, Status: Complete, Tests: All passing\"</em>\n\nThe deploy was led by <strong>Sarah Chen</strong> and completed successfully."
1410
+ }
1411
+ };
1412
+
1413
+ // ═══════════════════════════════════════════════════════════════
1414
+ // Animation Helpers
1415
+ // ═══════════════════════════════════════════════════════════════
1416
+ let speedMultiplier = 1;
1417
+ let currentScenario = 'search';
1418
+
1419
+ const sleep = ms => new Promise(r => setTimeout(r, ms / speedMultiplier));
1420
+
1421
+ function updateSpeed(value) {
1422
+ speedMultiplier = parseFloat(value);
1423
+ }
1424
+
1425
+ function replayScenario() {
1426
+ runScenario(currentScenario);
1427
+ }
1428
+
1429
+ async function typeText(element, text, speed = 25) {
1430
+ // Convert \n\n to proper line breaks for final display
1431
+ const formattedText = text.replace(/\n\n/g, '<br><br>').replace(/\n/g, '<br>');
1432
+
1433
+ // For short text or fast speed, just fade in
1434
+ if (speedMultiplier >= 1.5 || text.length < 20) {
1435
+ element.style.opacity = '0';
1436
+ element.innerHTML = formattedText;
1437
+ element.style.transition = 'opacity 0.3s ease-out';
1438
+ await sleep(50);
1439
+ element.style.opacity = '1';
1440
+ return;
1441
+ }
1442
+
1443
+ // Character-by-character typing with cursor
1444
+ element.innerHTML = '<span class="typing-cursor"></span>';
1445
+ const cursor = element.querySelector('.typing-cursor');
1446
+
1447
+ // Parse HTML and type text content while preserving tags
1448
+ const tempDiv = document.createElement('div');
1449
+ tempDiv.innerHTML = formattedText;
1450
+ const plainText = tempDiv.textContent;
1451
+
1452
+ let currentIndex = 0;
1453
+ const textSpan = document.createElement('span');
1454
+ element.insertBefore(textSpan, cursor);
1455
+
1456
+ for (const char of plainText) {
1457
+ textSpan.textContent += char;
1458
+ await sleep(speed);
1459
+ }
1460
+
1461
+ // Replace with formatted HTML and remove cursor
1462
+ await sleep(100);
1463
+ element.innerHTML = formattedText;
1464
+ }
1465
+
1466
+ // Claude's sparkle icon SVG
1467
+ const claudeIcon = `<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2L9.5 9.5L2 12L9.5 14.5L12 22L14.5 14.5L22 12L14.5 9.5L12 2Z"/></svg>`;
1468
+
1469
+ function createMessage(type, sender, time) {
1470
+ const msg = document.createElement('div');
1471
+ msg.className = `message ${type}`;
1472
+ const avatar = type === 'user' ? 'Y' : claudeIcon;
1473
+ msg.innerHTML = `
1474
+ <div class="message-header">
1475
+ <div class="message-avatar">${avatar}</div>
1476
+ <span class="message-sender">${sender}</span>
1477
+ <span class="message-time">${time}</span>
1478
+ </div>
1479
+ <div class="message-content"></div>
1480
+ `;
1481
+ return msg;
1482
+ }
1483
+
1484
+ function createTypingIndicator() {
1485
+ const indicator = document.createElement('div');
1486
+ indicator.className = 'message claude';
1487
+ indicator.id = 'typingIndicator';
1488
+ indicator.innerHTML = `
1489
+ <div class="message-header">
1490
+ <div class="message-avatar">${claudeIcon}</div>
1491
+ <span class="message-sender">Claude</span>
1492
+ </div>
1493
+ <div class="typing-indicator">
1494
+ <div class="typing-dots">
1495
+ <div class="typing-dot"></div>
1496
+ <div class="typing-dot"></div>
1497
+ <div class="typing-dot"></div>
1498
+ </div>
1499
+ </div>
1500
+ `;
1501
+ return indicator;
1502
+ }
1503
+
1504
+ function createToolCall(tool, isRunning = true) {
1505
+ const toolEl = document.createElement('div');
1506
+ toolEl.className = 'tool-call';
1507
+
1508
+ let resultHtml = '';
1509
+ if (Array.isArray(tool.result)) {
1510
+ resultHtml = tool.result.map(item => {
1511
+ if (item.channel) {
1512
+ return `<div class="result-item">
1513
+ <span class="result-channel">${item.channel}</span> ·
1514
+ <span class="result-user">${item.user}</span> ·
1515
+ <span class="result-time">${item.time}</span><br>
1516
+ <em>"${item.text}"</em>
1517
+ </div>`;
1518
+ } else if (item.name) {
1519
+ return `<div class="result-item">
1520
+ <span class="result-user">${item.name}</span> ·
1521
+ <span class="result-time">${item.lastMessage}</span><br>
1522
+ <em>"${item.preview}"</em>
1523
+ </div>`;
1524
+ } else {
1525
+ return `<div class="result-item">
1526
+ <span class="result-user">${item.user}</span> ·
1527
+ <span class="result-time">${item.time}</span><br>
1528
+ "${item.text}"
1529
+ </div>`;
1530
+ }
1531
+ }).join('');
1532
+ } else if (tool.result.success) {
1533
+ resultHtml = `<div class="result-item">
1534
+ ✅ Message sent to <span class="result-channel">${tool.result.channel}</span>
1535
+ </div>`;
1536
+ }
1537
+
1538
+ const statusClass = isRunning ? 'running' : 'success';
1539
+ const statusText = isRunning ? 'Running...' : 'Complete';
1540
+
1541
+ toolEl.innerHTML = `
1542
+ <div class="tool-header" onclick="this.parentElement.classList.toggle('expanded')">
1543
+ <span class="tool-icon">🔧</span>
1544
+ <span class="tool-name">${tool.name}</span>
1545
+ <span class="tool-status ${statusClass}">${statusText}</span>
1546
+ <span class="tool-chevron">▼</span>
1547
+ </div>
1548
+ <div class="tool-body">
1549
+ <div class="tool-section">
1550
+ <div class="tool-section-label">Input</div>
1551
+ <div class="tool-params">${JSON.stringify(tool.params, null, 2)}</div>
1552
+ </div>
1553
+ <div class="tool-section">
1554
+ <div class="tool-section-label">Output</div>
1555
+ <div class="tool-result">${resultHtml}</div>
1556
+ </div>
1557
+ </div>
1558
+ `;
1559
+ return toolEl;
1560
+ }
1561
+
1562
+ function updateToolStatus(toolEl, isComplete) {
1563
+ const statusEl = toolEl.querySelector('.tool-status');
1564
+ statusEl.className = `tool-status ${isComplete ? 'success' : 'running'}`;
1565
+ statusEl.textContent = isComplete ? 'Complete' : 'Running...';
1566
+ }
1567
+
1568
+ // ═══════════════════════════════════════════════════════════════
1569
+ // Run Scenario
1570
+ // ═══════════════════════════════════════════════════════════════
1571
+ let isRunning = false;
1572
+
1573
+ async function runScenario(scenarioId) {
1574
+ if (isRunning) return;
1575
+ isRunning = true;
1576
+ currentScenario = scenarioId;
1577
+
1578
+ // Update button states
1579
+ const replayBtn = document.getElementById('replayBtn');
1580
+ if (replayBtn) replayBtn.disabled = true;
1581
+
1582
+ document.querySelectorAll('.scenario-btn').forEach(btn => {
1583
+ const isActive = btn.dataset.scenario === scenarioId;
1584
+ btn.classList.toggle('active', isActive);
1585
+ btn.classList.toggle('playing', isActive);
1586
+ btn.setAttribute('aria-selected', isActive ? 'true' : 'false');
1587
+ });
1588
+
1589
+ const container = document.getElementById('chatContainer');
1590
+
1591
+ // Show scenario caption
1592
+ const captions = {
1593
+ search: "🔍 Searching Messages",
1594
+ thread: "📜 Reading Thread",
1595
+ list: "💬 Listing DMs",
1596
+ send: "✉️ Sending Message",
1597
+ multi: "🔗 Multi-Tool Workflow"
1598
+ };
1599
+ const caption = document.getElementById('scenarioCaption');
1600
+ caption.textContent = captions[scenarioId] || scenarioId;
1601
+ caption.classList.add('visible');
1602
+ setTimeout(() => caption.classList.remove('visible'), 2000);
1603
+
1604
+ // Smooth transition: fade out, clear, fade in
1605
+ container.classList.add('fading');
1606
+ await sleep(300);
1607
+ container.innerHTML = '';
1608
+ container.classList.remove('fading');
1609
+
1610
+ const scenario = scenarios[scenarioId];
1611
+ const now = new Date();
1612
+ const timeStr = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' });
1613
+
1614
+ // 1. User message appears
1615
+ const userMsg = createMessage('user', 'You', timeStr);
1616
+ container.appendChild(userMsg);
1617
+ userMsg.querySelector('.message-content').textContent = scenario.userMessage;
1618
+ container.scrollTop = container.scrollHeight;
1619
+
1620
+ await sleep(600);
1621
+
1622
+ // 2. Claude starts typing
1623
+ const typing = createTypingIndicator();
1624
+ container.appendChild(typing);
1625
+ container.scrollTop = container.scrollHeight;
1626
+
1627
+ await sleep(1200);
1628
+
1629
+ // 3. Claude's intro
1630
+ typing.remove();
1631
+ const claudeMsg = createMessage('claude', 'Claude', timeStr);
1632
+ container.appendChild(claudeMsg);
1633
+ const contentEl = claudeMsg.querySelector('.message-content');
1634
+ await typeText(contentEl, scenario.claudeIntro);
1635
+ container.scrollTop = container.scrollHeight;
1636
+
1637
+ await sleep(700);
1638
+
1639
+ // 4. Tool call(s) - handle single or multi-tool scenarios
1640
+ if (scenario.isMultiTool && scenario.toolCalls) {
1641
+ // Multi-tool scenario
1642
+ for (let i = 0; i < scenario.toolCalls.length; i++) {
1643
+ const tool = scenario.toolCalls[i];
1644
+ const toolCall = createToolCall(tool, true);
1645
+ contentEl.appendChild(toolCall);
1646
+ container.scrollTop = container.scrollHeight;
1647
+
1648
+ await sleep(400);
1649
+ toolCall.classList.add('expanded');
1650
+ container.scrollTop = container.scrollHeight;
1651
+
1652
+ await sleep(1200);
1653
+ updateToolStatus(toolCall, true);
1654
+ container.scrollTop = container.scrollHeight;
1655
+
1656
+ if (i < scenario.toolCalls.length - 1) {
1657
+ await sleep(600); // Pause between tools
1658
+ }
1659
+ }
1660
+ } else {
1661
+ // Single tool scenario
1662
+ const toolCall = createToolCall(scenario.toolCall, true);
1663
+ contentEl.appendChild(toolCall);
1664
+ container.scrollTop = container.scrollHeight;
1665
+
1666
+ await sleep(400);
1667
+ toolCall.classList.add('expanded');
1668
+ container.scrollTop = container.scrollHeight;
1669
+
1670
+ await sleep(1500);
1671
+ updateToolStatus(toolCall, true);
1672
+ container.scrollTop = container.scrollHeight;
1673
+ }
1674
+
1675
+ await sleep(500);
1676
+
1677
+ // 5. Claude's summary
1678
+ const summaryP = document.createElement('div');
1679
+ summaryP.style.marginTop = '16px';
1680
+ contentEl.appendChild(summaryP);
1681
+ await typeText(summaryP, scenario.claudeSummary);
1682
+ container.scrollTop = container.scrollHeight;
1683
+
1684
+ // Cleanup
1685
+ document.querySelectorAll('.scenario-btn').forEach(btn => {
1686
+ btn.classList.remove('playing');
1687
+ });
1688
+ if (replayBtn) replayBtn.disabled = false;
1689
+ isRunning = false;
1690
+ }
1691
+
1692
+ // ═══════════════════════════════════════════════════════════════
1693
+ // Auto-Play All Scenarios
1694
+ // ═══════════════════════════════════════════════════════════════
1695
+ let isAutoPlaying = false;
1696
+
1697
+ async function autoPlayAll() {
1698
+ if (isAutoPlaying || isRunning) return;
1699
+ isAutoPlaying = true;
1700
+
1701
+ const autoPlayBtn = document.getElementById('autoPlayBtn');
1702
+ if (autoPlayBtn) {
1703
+ autoPlayBtn.innerHTML = '<span>⏹</span><span>Stop</span>';
1704
+ autoPlayBtn.onclick = stopAutoPlay;
1705
+ }
1706
+
1707
+ // Show title card for 3s before starting
1708
+ const titleCard = document.getElementById('titleCard');
1709
+ const chatContainer = document.getElementById('chatContainer');
1710
+ chatContainer.style.display = 'none';
1711
+ titleCard.classList.add('visible');
1712
+ await sleep(3000);
1713
+ titleCard.classList.remove('visible');
1714
+ await sleep(500); // Fade transition
1715
+ chatContainer.style.display = '';
1716
+
1717
+ const scenarioOrder = ['search', 'thread', 'list', 'send', 'multi'];
1718
+
1719
+ for (let i = 0; i < scenarioOrder.length; i++) {
1720
+ if (!isAutoPlaying) break;
1721
+ updateProgress(i + 1, scenarioOrder.length);
1722
+ await runScenario(scenarioOrder[i]);
1723
+ if (!isAutoPlaying) break;
1724
+ await sleep(2000); // Pause between scenarios
1725
+ }
1726
+ updateProgress(0, 0); // Clear progress
1727
+
1728
+ // Show closing card for 4s after completion (only if not stopped)
1729
+ if (isAutoPlaying) {
1730
+ const closingCard = document.getElementById('closingCard');
1731
+ chatContainer.style.display = 'none';
1732
+ await sleep(500);
1733
+ closingCard.classList.add('visible');
1734
+ await sleep(4000);
1735
+ closingCard.classList.remove('visible');
1736
+ await sleep(500);
1737
+ chatContainer.style.display = '';
1738
+ }
1739
+
1740
+ stopAutoPlay();
1741
+ }
1742
+
1743
+ function stopAutoPlay() {
1744
+ isAutoPlaying = false;
1745
+ const autoPlayBtn = document.getElementById('autoPlayBtn');
1746
+ if (autoPlayBtn) {
1747
+ autoPlayBtn.innerHTML = '<span>▶</span><span>Auto-Play All</span>';
1748
+ autoPlayBtn.onclick = autoPlayAll;
1749
+ }
1750
+ updateProgress(0, 0);
1751
+ }
1752
+
1753
+ function updateProgress(current, total) {
1754
+ const indicator = document.getElementById('progressIndicator');
1755
+ if (!indicator) return;
1756
+
1757
+ if (current === 0 || total === 0) {
1758
+ indicator.style.display = 'none';
1759
+ return;
1760
+ }
1761
+
1762
+ indicator.style.display = 'flex';
1763
+ indicator.querySelector('.progress-text').textContent = `${current}/${total}`;
1764
+ indicator.querySelector('.progress-fill').style.width = `${(current / total) * 100}%`;
1765
+ }
1766
+
1767
+ async function copyShareLink() {
1768
+ const btn = document.querySelector('.share-btn');
1769
+ const url = window.location.href.split('?')[0]; // Remove any query params
1770
+
1771
+ try {
1772
+ await navigator.clipboard.writeText(url);
1773
+ btn.classList.add('copied');
1774
+ btn.querySelector('.share-text').textContent = 'Copied!';
1775
+ btn.querySelector('.share-icon').textContent = '✓';
1776
+
1777
+ setTimeout(() => {
1778
+ btn.classList.remove('copied');
1779
+ btn.querySelector('.share-text').textContent = 'Share';
1780
+ btn.querySelector('.share-icon').textContent = '🔗';
1781
+ }, 2000);
1782
+ } catch (err) {
1783
+ // Fallback for older browsers
1784
+ const textarea = document.createElement('textarea');
1785
+ textarea.value = url;
1786
+ document.body.appendChild(textarea);
1787
+ textarea.select();
1788
+ document.execCommand('copy');
1789
+ document.body.removeChild(textarea);
1790
+ }
1791
+ }
1792
+
1793
+ // ═══════════════════════════════════════════════════════════════
1794
+ // Keyboard Shortcuts
1795
+ // ═══════════════════════════════════════════════════════════════
1796
+ function toggleFullscreen() {
1797
+ document.body.classList.toggle('fullscreen-mode');
1798
+ }
1799
+
1800
+ document.addEventListener('keydown', (e) => {
1801
+ // Fullscreen can toggle anytime
1802
+ if (e.key === 'f' || e.key === 'F') {
1803
+ toggleFullscreen();
1804
+ return;
1805
+ }
1806
+
1807
+ // Escape exits fullscreen first, then stops auto-play
1808
+ if (e.key === 'Escape') {
1809
+ if (document.body.classList.contains('fullscreen-mode')) {
1810
+ toggleFullscreen();
1811
+ } else {
1812
+ stopAutoPlay();
1813
+ }
1814
+ return;
1815
+ }
1816
+
1817
+ if (isRunning) return;
1818
+
1819
+ switch(e.key) {
1820
+ case '1': runScenario('search'); break;
1821
+ case '2': runScenario('thread'); break;
1822
+ case '3': runScenario('list'); break;
1823
+ case '4': runScenario('send'); break;
1824
+ case '5': runScenario('multi'); break;
1825
+ case 'r': case 'R': replayScenario(); break;
1826
+ case 'a': case 'A': autoPlayAll(); break;
1827
+ }
1828
+ });
1829
+
1830
+ // ═══════════════════════════════════════════════════════════════
1831
+ // Initialize
1832
+ // ═══════════════════════════════════════════════════════════════
1833
+ document.addEventListener('DOMContentLoaded', () => {
1834
+ runScenario('search');
1835
+ });
1836
+ </script>
1837
+ </body>
1838
+ </html>