@jtalk22/slack-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,920 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Slack MCP Server - Demo</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
10
+ <style>
11
+ :root {
12
+ --bg-primary: #0f0f1a;
13
+ --bg-secondary: #1a1a2e;
14
+ --bg-tertiary: #16213e;
15
+ --bg-input: #0f3460;
16
+ --accent: #4ecdc4;
17
+ --accent-hover: #45b7aa;
18
+ --accent-glow: rgba(78, 205, 196, 0.3);
19
+ --danger: #e94560;
20
+ --text-primary: #ffffff;
21
+ --text-secondary: rgba(255, 255, 255, 0.7);
22
+ --text-muted: rgba(255, 255, 255, 0.5);
23
+ --border-radius: 12px;
24
+ --transition: all 0.2s ease;
25
+ }
26
+
27
+ * { box-sizing: border-box; margin: 0; padding: 0; }
28
+
29
+ body {
30
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
31
+ background: var(--bg-primary);
32
+ color: var(--text-primary);
33
+ min-height: 100vh;
34
+ line-height: 1.5;
35
+ }
36
+
37
+ /* Gradient background */
38
+ .bg-gradient {
39
+ position: fixed;
40
+ 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;
55
+ }
56
+
57
+ /* Header */
58
+ .header {
59
+ 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);
65
+ }
66
+
67
+ .logo {
68
+ display: flex;
69
+ align-items: center;
70
+ gap: 12px;
71
+ }
72
+
73
+ .logo-icon {
74
+ width: 48px;
75
+ height: 48px;
76
+ background: linear-gradient(135deg, var(--accent), #7c3aed);
77
+ border-radius: 12px;
78
+ display: flex;
79
+ align-items: center;
80
+ 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
+ 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;
92
+ }
93
+
94
+ .logo .subtitle {
95
+ font-size: 13px;
96
+ color: var(--text-muted);
97
+ font-weight: 400;
98
+ }
99
+
100
+ .demo-badge {
101
+ background: linear-gradient(135deg, var(--danger), #ff8c00);
102
+ color: white;
103
+ padding: 6px 16px;
104
+ border-radius: 20px;
105
+ font-size: 12px;
106
+ font-weight: 600;
107
+ text-transform: uppercase;
108
+ letter-spacing: 0.5px;
109
+ animation: pulse 2s infinite;
110
+ }
111
+
112
+ @keyframes pulse {
113
+ 0%, 100% { opacity: 1; }
114
+ 50% { opacity: 0.7; }
115
+ }
116
+
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);
127
+ }
128
+
129
+ .status-indicator {
130
+ display: flex;
131
+ align-items: center;
132
+ gap: 10px;
133
+ }
134
+
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;
142
+ }
143
+
144
+ @keyframes blink {
145
+ 0%, 100% { opacity: 1; }
146
+ 50% { opacity: 0.5; }
147
+ }
148
+
149
+ .status-text {
150
+ font-size: 14px;
151
+ color: var(--text-secondary);
152
+ }
153
+
154
+ .status-text strong {
155
+ color: var(--accent);
156
+ }
157
+
158
+ .workspace-info {
159
+ display: flex;
160
+ align-items: center;
161
+ gap: 16px;
162
+ font-size: 13px;
163
+ color: var(--text-muted);
164
+ }
165
+
166
+ .workspace-info span {
167
+ display: flex;
168
+ align-items: center;
169
+ gap: 6px;
170
+ }
171
+
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;
179
+ }
180
+
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;
189
+ }
190
+
191
+ .sidebar-header {
192
+ padding: 20px;
193
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
194
+ }
195
+
196
+ .sidebar-header h3 {
197
+ font-size: 11px;
198
+ font-weight: 600;
199
+ text-transform: uppercase;
200
+ letter-spacing: 1px;
201
+ color: var(--text-muted);
202
+ margin-bottom: 12px;
203
+ }
204
+
205
+ .tabs {
206
+ display: flex;
207
+ gap: 8px;
208
+ }
209
+
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;
217
+ cursor: pointer;
218
+ font-size: 13px;
219
+ font-weight: 500;
220
+ transition: var(--transition);
221
+ font-family: inherit;
222
+ }
223
+
224
+ .tabs button:hover {
225
+ background: rgba(78, 205, 196, 0.1);
226
+ color: var(--accent);
227
+ }
228
+
229
+ .tabs button.active {
230
+ background: var(--accent);
231
+ color: var(--bg-primary);
232
+ }
233
+
234
+ .conversation-list {
235
+ list-style: none;
236
+ flex: 1;
237
+ overflow-y: auto;
238
+ padding: 12px;
239
+ }
240
+
241
+ .conversation-list::-webkit-scrollbar {
242
+ width: 6px;
243
+ }
244
+
245
+ .conversation-list::-webkit-scrollbar-track {
246
+ background: transparent;
247
+ }
248
+
249
+ .conversation-list::-webkit-scrollbar-thumb {
250
+ background: rgba(255, 255, 255, 0.1);
251
+ border-radius: 3px;
252
+ }
253
+
254
+ .conversation-item {
255
+ padding: 14px 16px;
256
+ border-radius: 10px;
257
+ cursor: pointer;
258
+ margin-bottom: 6px;
259
+ background: transparent;
260
+ transition: var(--transition);
261
+ display: flex;
262
+ align-items: center;
263
+ gap: 12px;
264
+ }
265
+
266
+ .conversation-item:hover {
267
+ background: rgba(255, 255, 255, 0.05);
268
+ }
269
+
270
+ .conversation-item.active {
271
+ background: linear-gradient(135deg, var(--accent), #3b82f6);
272
+ box-shadow: 0 4px 15px var(--accent-glow);
273
+ }
274
+
275
+ .conversation-item.active .conv-name,
276
+ .conversation-item.active .conv-type {
277
+ color: var(--bg-primary);
278
+ }
279
+
280
+ .avatar {
281
+ width: 40px;
282
+ height: 40px;
283
+ border-radius: 10px;
284
+ display: flex;
285
+ align-items: center;
286
+ justify-content: center;
287
+ font-weight: 600;
288
+ font-size: 14px;
289
+ flex-shrink: 0;
290
+ }
291
+
292
+ .avatar.dm { background: linear-gradient(135deg, #8b5cf6, #ec4899); }
293
+ .avatar.channel { background: linear-gradient(135deg, #3b82f6, #06b6d4); }
294
+ .avatar.group { background: linear-gradient(135deg, #f59e0b, #ef4444); }
295
+
296
+ .conv-info {
297
+ flex: 1;
298
+ min-width: 0;
299
+ }
300
+
301
+ .conv-name {
302
+ font-weight: 500;
303
+ font-size: 14px;
304
+ color: var(--text-primary);
305
+ white-space: nowrap;
306
+ overflow: hidden;
307
+ text-overflow: ellipsis;
308
+ }
309
+
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);
332
+ display: flex;
333
+ flex-direction: column;
334
+ overflow: hidden;
335
+ }
336
+
337
+ .panel-header {
338
+ padding: 20px 24px;
339
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
340
+ display: flex;
341
+ align-items: center;
342
+ justify-content: space-between;
343
+ }
344
+
345
+ .channel-title {
346
+ display: flex;
347
+ 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);
355
+ }
356
+
357
+ .channel-title .online-status {
358
+ width: 8px;
359
+ height: 8px;
360
+ background: #22c55e;
361
+ border-radius: 50%;
362
+ }
363
+
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 {
411
+ flex: 1;
412
+ 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;
423
+ }
424
+
425
+ .message {
426
+ display: flex;
427
+ gap: 16px;
428
+ padding: 16px;
429
+ border-radius: 12px;
430
+ margin-bottom: 8px;
431
+ transition: var(--transition);
432
+ animation: fadeIn 0.3s ease;
433
+ }
434
+
435
+ @keyframes fadeIn {
436
+ from { opacity: 0; transform: translateY(10px); }
437
+ to { opacity: 1; transform: translateY(0); }
438
+ }
439
+
440
+ .message:hover {
441
+ background: rgba(255, 255, 255, 0.03);
442
+ }
443
+
444
+ .message-avatar {
445
+ width: 44px;
446
+ height: 44px;
447
+ border-radius: 12px;
448
+ display: flex;
449
+ align-items: center;
450
+ justify-content: center;
451
+ font-weight: 600;
452
+ font-size: 16px;
453
+ flex-shrink: 0;
454
+ }
455
+
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
+ }
485
+
486
+ .message-text code {
487
+ background: rgba(0, 0, 0, 0.3);
488
+ padding: 2px 6px;
489
+ border-radius: 4px;
490
+ font-family: 'SF Mono', 'Fira Code', monospace;
491
+ font-size: 13px;
492
+ color: #f472b6;
493
+ }
494
+
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);
502
+ }
503
+
504
+ .message-text pre code {
505
+ background: none;
506
+ padding: 0;
507
+ color: #e2e8f0;
508
+ display: block;
509
+ white-space: pre;
510
+ }
511
+
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;
523
+ display: flex;
524
+ align-items: center;
525
+ gap: 6px;
526
+ cursor: pointer;
527
+ transition: var(--transition);
528
+ }
529
+
530
+ .reaction:hover {
531
+ background: rgba(255, 255, 255, 0.1);
532
+ }
533
+
534
+ .reaction-count {
535
+ 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);
556
+ }
557
+
558
+ /* Send box */
559
+ .send-box {
560
+ padding: 20px 24px;
561
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
562
+ display: flex;
563
+ gap: 12px;
564
+ }
565
+
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);
576
+ }
577
+
578
+ .send-box input:focus {
579
+ outline: none;
580
+ border-color: var(--accent);
581
+ box-shadow: 0 0 0 3px var(--accent-glow);
582
+ }
583
+
584
+ .send-box input::placeholder {
585
+ color: var(--text-muted);
586
+ }
587
+
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 {
609
+ text-align: center;
610
+ padding: 24px;
611
+ color: var(--text-muted);
612
+ font-size: 13px;
613
+ }
614
+
615
+ .footer a {
616
+ color: var(--accent);
617
+ text-decoration: none;
618
+ }
619
+
620
+ .footer a:hover {
621
+ text-decoration: underline;
622
+ }
623
+
624
+ /* Mobile responsive */
625
+ @media (max-width: 900px) {
626
+ .grid {
627
+ grid-template-columns: 1fr;
628
+ height: auto;
629
+ }
630
+ .sidebar {
631
+ max-height: 300px;
632
+ }
633
+ .main-panel {
634
+ min-height: 500px;
635
+ }
636
+ .search-box input {
637
+ width: 150px;
638
+ }
639
+ }
640
+ </style>
641
+ </head>
642
+ <body>
643
+ <div class="bg-gradient"></div>
644
+
645
+ <div class="container">
646
+ <header class="header">
647
+ <div class="logo">
648
+ <div class="logo-icon">S</div>
649
+ <div>
650
+ <h1>Slack MCP Server</h1>
651
+ <div class="subtitle">Model Context Protocol for Slack</div>
652
+ </div>
653
+ </div>
654
+ <div class="demo-badge">Interactive Demo</div>
655
+ </header>
656
+
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>
661
+ </div>
662
+ <div class="workspace-info">
663
+ <span>12 conversations</span>
664
+ <span>|</span>
665
+ <span>Last sync: just now</span>
666
+ </div>
667
+ </div>
668
+
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>
693
+ </div>
694
+
695
+ <div id="messages" class="messages">
696
+ <!-- Populated by JS -->
697
+ </div>
698
+
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>
704
+ </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
+ </div>
710
+
711
+ <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: [] },
736
+ ],
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: [] },
741
+ ],
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
+ ]
749
+ };
750
+
751
+ let currentView = 'dms';
752
+ let currentChannel = 'D001';
753
+
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>
759
+ <div class="conv-info">
760
+ <div class="conv-name">${c.name}</div>
761
+ <div class="conv-type">${c.type === 'dm' ? 'Direct message' : 'Channel'}</div>
762
+ </div>
763
+ ${c.unread > 0 ? `<div class="unread-badge">${c.unread}</div>` : ''}
764
+ </li>
765
+ `).join('');
766
+ }
767
+
768
+ function renderMessages(channelId) {
769
+ const container = document.getElementById('messages');
770
+ const messages = mockMessages[channelId] || [];
771
+
772
+ if (messages.length === 0) {
773
+ container.innerHTML = '<div style="text-align: center; padding: 40px; color: var(--text-muted);">No messages yet</div>';
774
+ return;
775
+ }
776
+
777
+ 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>
780
+ <div class="message-content">
781
+ <div class="message-header">
782
+ <span class="message-user">${m.user}</span>
783
+ <span class="message-time">${m.time}</span>
784
+ </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
+ ` : ''}
797
+ </div>
798
+ </div>
799
+ `).join('');
800
+
801
+ container.scrollTop = container.scrollHeight;
802
+ }
803
+
804
+ function formatMessageText(text) {
805
+ // Convert multiline code blocks to <pre><code>
806
+ text = text.replace(/```(\w+)?\n([\s\S]*?)```/g, '<pre><code>$2</code></pre>');
807
+ // Convert inline code to <code>
808
+ text = text.replace(/`([^`]+)`/g, '<code>$1</code>');
809
+ return text;
810
+ }
811
+
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;
824
+ }
825
+
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(', ');
836
+ }
837
+
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);
843
+ }
844
+
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);
850
+ }
851
+
852
+ function selectConversation(id, name) {
853
+ currentChannel = id;
854
+ document.getElementById('channelName').textContent = name;
855
+
856
+ // Update active state
857
+ document.querySelectorAll('.conversation-item').forEach(li => li.classList.remove('active'));
858
+ event.target.closest('.conversation-item').classList.add('active');
859
+
860
+ renderMessages(id);
861
+ }
862
+
863
+ function searchDemo() {
864
+ const query = document.getElementById('searchQuery').value;
865
+ if (!query) return;
866
+
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>
878
+ </div>
879
+ `;
880
+ }
881
+
882
+ function sendDemo() {
883
+ const input = document.getElementById('messageInput');
884
+ const text = input.value.trim();
885
+ if (!text) return;
886
+
887
+ const container = document.getElementById('messages');
888
+ const now = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
889
+
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>
899
+ </div>
900
+ </div>
901
+ `;
902
+
903
+ input.value = '';
904
+ container.scrollTop = container.scrollHeight;
905
+ }
906
+
907
+ // Initialize
908
+ renderConversations(mockDMs);
909
+ renderMessages('D001');
910
+
911
+ // Handle enter key
912
+ document.getElementById('messageInput').addEventListener('keypress', (e) => {
913
+ if (e.key === 'Enter') sendDemo();
914
+ });
915
+ document.getElementById('searchQuery').addEventListener('keypress', (e) => {
916
+ if (e.key === 'Enter') searchDemo();
917
+ });
918
+ </script>
919
+ </body>
920
+ </html>