@jtalk22/slack-mcp 2.0.0 → 3.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.
Files changed (55) hide show
  1. package/README.md +112 -64
  2. package/docs/CLOUDFLARE-BROWSER-TOOLKIT.md +67 -0
  3. package/docs/DEPLOYMENT-MODES.md +10 -3
  4. package/docs/HN-LAUNCH.md +47 -36
  5. package/docs/INDEX.md +4 -1
  6. package/docs/INSTALL-PROOF.md +5 -5
  7. package/docs/LAUNCH-COPY-v3.0.0.md +73 -0
  8. package/docs/LAUNCH-MATRIX.md +4 -2
  9. package/docs/LAUNCH-OPS.md +24 -23
  10. package/docs/RELEASE-HEALTH.md +9 -0
  11. package/docs/TROUBLESHOOTING.md +27 -0
  12. package/docs/WEB-API.md +13 -4
  13. package/docs/images/demo-channel-messages.png +0 -0
  14. package/docs/images/demo-channels.png +0 -0
  15. package/docs/images/demo-claude-mobile-360x800.png +0 -0
  16. package/docs/images/demo-claude-mobile-390x844.png +0 -0
  17. package/docs/images/demo-main-mobile-360x800.png +0 -0
  18. package/docs/images/demo-main-mobile-390x844.png +0 -0
  19. package/docs/images/demo-main.png +0 -0
  20. package/docs/images/demo-poster.png +0 -0
  21. package/docs/images/demo-sidebar.png +0 -0
  22. package/docs/images/web-api-mobile-360x800.png +0 -0
  23. package/docs/images/web-api-mobile-390x844.png +0 -0
  24. package/package.json +14 -6
  25. package/public/demo-claude.html +83 -10
  26. package/public/demo-video.html +33 -4
  27. package/public/demo.html +136 -2
  28. package/public/index.html +132 -69
  29. package/scripts/capture-screenshots.js +103 -53
  30. package/scripts/check-version-parity.js +25 -11
  31. package/scripts/cloudflare-browser-tool.js +237 -0
  32. package/scripts/collect-release-health.js +1 -1
  33. package/scripts/record-demo.js +22 -9
  34. package/scripts/release-preflight.js +243 -0
  35. package/scripts/setup-wizard.js +1 -1
  36. package/scripts/verify-install-flow.js +2 -1
  37. package/scripts/verify-web.js +49 -1
  38. package/server.json +47 -0
  39. package/smithery.yaml +34 -0
  40. package/src/server-http.js +98 -5
  41. package/src/server.js +18 -6
  42. package/src/web-server.js +5 -3
  43. package/docs/LAUNCH-COPY-v2.0.0.md +0 -59
  44. package/docs/images/demo-claude-v1.2.gif +0 -0
  45. package/docs/images/demo-readme.gif +0 -0
  46. package/docs/release-health/2026-02-25.md +0 -33
  47. package/docs/release-health/2026-02-26.md +0 -33
  48. package/docs/release-health/24h-delta.md +0 -21
  49. package/docs/release-health/24h-end.md +0 -33
  50. package/docs/release-health/24h-start.md +0 -33
  51. package/docs/release-health/latest.md +0 -33
  52. package/docs/release-health/launch-log-template.md +0 -21
  53. package/docs/release-health/version-parity.md +0 -21
  54. package/docs/videos/.gitkeep +0 -0
  55. package/docs/videos/demo-claude-v1.2.webm +0 -0
package/public/demo.html CHANGED
@@ -22,9 +22,11 @@
22
22
  <meta name="description" content="Session-based Slack access for Claude with your existing workspace permissions. Interactive demo for DMs, channels, search, and thread workflows.">
23
23
  <link rel="preconnect" href="https://fonts.googleapis.com">
24
24
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
25
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
25
+ <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=Space+Grotesk:wght@500;600;700&display=swap" rel="stylesheet">
26
26
  <style>
27
27
  :root {
28
+ --font-heading: "Space Grotesk", "Avenir Next", "Segoe UI", sans-serif;
29
+ --font-body: "IBM Plex Sans", "Inter", "Segoe UI", sans-serif;
28
30
  --bg-primary: #0f0f1a;
29
31
  --bg-secondary: #1a1a2e;
30
32
  --bg-tertiary: #16213e;
@@ -45,7 +47,7 @@
45
47
  * { box-sizing: border-box; margin: 0; padding: 0; }
46
48
 
47
49
  body {
48
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
50
+ font-family: var(--font-body);
49
51
  background: var(--bg-primary);
50
52
  color: var(--text-primary);
51
53
  min-height: 100vh;
@@ -150,6 +152,7 @@
150
152
  font-size: 16px;
151
153
  font-weight: 600;
152
154
  color: var(--text-primary);
155
+ font-family: var(--font-heading);
153
156
  }
154
157
  .claude-header .subtitle {
155
158
  font-size: 12px;
@@ -276,6 +279,7 @@
276
279
  letter-spacing: 1px;
277
280
  color: var(--text-muted);
278
281
  margin-bottom: 12px;
282
+ font-family: var(--font-heading);
279
283
  }
280
284
 
281
285
  .try-buttons .scenarios {
@@ -352,6 +356,7 @@
352
356
  .dashboard-title h2 {
353
357
  font-size: 18px;
354
358
  font-weight: 600;
359
+ font-family: var(--font-heading);
355
360
  }
356
361
 
357
362
  .status-badge {
@@ -475,6 +480,7 @@
475
480
  display: flex;
476
481
  align-items: center;
477
482
  gap: 8px;
483
+ font-family: var(--font-heading);
478
484
  }
479
485
 
480
486
  .online-dot {
@@ -490,6 +496,75 @@
490
496
  padding: 20px 24px;
491
497
  }
492
498
 
499
+ .mobile-scenario-rail {
500
+ display: none;
501
+ padding: 14px 16px;
502
+ background: rgba(15, 52, 96, 0.55);
503
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
504
+ }
505
+
506
+ .mobile-rail-head {
507
+ display: flex;
508
+ align-items: center;
509
+ justify-content: space-between;
510
+ gap: 10px;
511
+ margin-bottom: 10px;
512
+ }
513
+
514
+ .mobile-rail-head h4 {
515
+ font-size: 12px;
516
+ letter-spacing: 0.08em;
517
+ text-transform: uppercase;
518
+ color: var(--text-muted);
519
+ font-family: var(--font-heading);
520
+ }
521
+
522
+ .mobile-scenario-buttons {
523
+ display: grid;
524
+ grid-template-columns: repeat(3, minmax(0, 1fr));
525
+ gap: 8px;
526
+ }
527
+
528
+ .mobile-scenario-btn {
529
+ background: rgba(255, 255, 255, 0.06);
530
+ color: var(--text-primary);
531
+ border: 1px solid rgba(255, 255, 255, 0.12);
532
+ border-radius: 10px;
533
+ padding: 10px 8px;
534
+ font-size: 12px;
535
+ font-weight: 600;
536
+ display: flex;
537
+ flex-direction: column;
538
+ align-items: center;
539
+ justify-content: center;
540
+ gap: 3px;
541
+ cursor: pointer;
542
+ transition: var(--transition);
543
+ text-align: center;
544
+ font-family: var(--font-body);
545
+ }
546
+
547
+ .mobile-scenario-btn:hover {
548
+ border-color: var(--accent);
549
+ background: rgba(78, 205, 196, 0.15);
550
+ }
551
+
552
+ .mobile-scenario-btn .icon {
553
+ font-size: 16px;
554
+ line-height: 1;
555
+ }
556
+
557
+ .mobile-cta {
558
+ margin-top: 10px;
559
+ font-size: 12px;
560
+ color: var(--text-secondary);
561
+ }
562
+
563
+ .mobile-reset {
564
+ padding: 4px 10px;
565
+ font-size: 11px;
566
+ }
567
+
493
568
  .message {
494
569
  display: flex;
495
570
  gap: 14px;
@@ -614,16 +689,53 @@
614
689
  @media (max-width: 1000px) {
615
690
  .split-container {
616
691
  grid-template-columns: 1fr;
692
+ height: auto;
693
+ min-height: calc(100vh - 88px);
617
694
  }
618
695
  .claude-panel {
619
696
  display: none;
620
697
  }
698
+ .mobile-scenario-rail {
699
+ display: block;
700
+ }
621
701
  .dashboard-content {
622
702
  grid-template-columns: 1fr;
623
703
  }
624
704
  .sidebar {
625
705
  display: none;
626
706
  }
707
+ .main-header {
708
+ padding: 14px 16px;
709
+ }
710
+ .messages-container {
711
+ padding: 16px;
712
+ }
713
+ }
714
+
715
+ @media (max-width: 640px) {
716
+ .cta-strip {
717
+ gap: 8px;
718
+ padding: 10px 12px;
719
+ }
720
+ .mobile-scenario-buttons {
721
+ grid-template-columns: repeat(2, minmax(0, 1fr));
722
+ }
723
+ .mobile-scenario-btn {
724
+ padding: 10px 6px;
725
+ }
726
+ .dashboard-header {
727
+ padding: 14px 12px;
728
+ }
729
+ .dashboard-title h2 {
730
+ font-size: 16px;
731
+ }
732
+ .status-badge {
733
+ font-size: 11px;
734
+ padding: 3px 10px;
735
+ }
736
+ .messages-container {
737
+ min-height: 48vh;
738
+ }
627
739
  }
628
740
  </style>
629
741
  </head>
@@ -706,6 +818,28 @@
706
818
  </div>
707
819
  </div>
708
820
 
821
+ <div class="mobile-scenario-rail" aria-label="Scenario shortcuts">
822
+ <div class="mobile-rail-head">
823
+ <h4>Try a Scenario</h4>
824
+ <button class="reset-btn mobile-reset" onclick="resetDemo()">Reset Demo</button>
825
+ </div>
826
+ <div class="mobile-scenario-buttons">
827
+ <button class="mobile-scenario-btn" onclick="runScenario('findKey')">
828
+ <span class="icon">🔑</span>
829
+ <span>Find Key</span>
830
+ </button>
831
+ <button class="mobile-scenario-btn" onclick="runScenario('listChannels')">
832
+ <span class="icon">📋</span>
833
+ <span>Channels</span>
834
+ </button>
835
+ <button class="mobile-scenario-btn" onclick="runScenario('whoIsAlex')">
836
+ <span class="icon">👤</span>
837
+ <span>Who is Alex?</span>
838
+ </button>
839
+ </div>
840
+ <p class="mobile-cta">Tap a scenario to drive the live preview with one click.</p>
841
+ </div>
842
+
709
843
  <div class="dashboard-content">
710
844
  <aside class="sidebar">
711
845
  <div class="sidebar-section">
package/public/index.html CHANGED
@@ -5,79 +5,92 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Slack Web API</title>
7
7
  <style>
8
+ @import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=Space+Grotesk:wght@500;600;700&display=swap');
9
+
10
+ :root {
11
+ --font-heading: "Space Grotesk", "Avenir Next", "Segoe UI", sans-serif;
12
+ --font-body: "IBM Plex Sans", "Inter", "Segoe UI", sans-serif;
13
+ --bg: #0f1736;
14
+ --panel: #16213e;
15
+ --panel-alt: #0f3460;
16
+ --teal: #4ecdc4;
17
+ --teal-hover: #45b7aa;
18
+ --danger: #ff6b6b;
19
+ --search: #e94560;
20
+ --text: #e8ecff;
21
+ --muted: #9fb0cf;
22
+ }
23
+
8
24
  * { box-sizing: border-box; margin: 0; padding: 0; }
9
25
  body {
10
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
11
- background: #1a1a2e;
12
- color: #eee;
26
+ font-family: var(--font-body);
27
+ background: radial-gradient(circle at 20% -10%, #1b2f67 0%, var(--bg) 48%);
28
+ color: var(--text);
13
29
  min-height: 100vh;
14
30
  padding: 20px;
31
+ overflow-x: hidden;
15
32
  }
16
33
  .container { max-width: 1200px; margin: 0 auto; }
17
- h1 { color: #4ecdc4; margin-bottom: 20px; }
18
- .auth-section {
19
- background: #16213e;
20
- padding: 20px;
21
- border-radius: 8px;
34
+ h1 {
35
+ color: var(--teal);
22
36
  margin-bottom: 20px;
37
+ font-family: var(--font-heading);
38
+ font-size: clamp(1.9rem, 2vw, 2.35rem);
39
+ line-height: 1.1;
40
+ letter-spacing: -0.02em;
23
41
  }
24
- .auth-section input {
25
- width: 300px;
26
- padding: 10px;
27
- border: none;
28
- border-radius: 4px;
29
- background: #0f3460;
30
- color: #fff;
31
- margin-right: 10px;
32
- }
33
- .auth-section button {
34
- padding: 10px 20px;
35
- background: #4ecdc4;
36
- color: #1a1a2e;
37
- border: none;
38
- border-radius: 4px;
39
- cursor: pointer;
40
- font-weight: bold;
41
- }
42
- .auth-section button:hover { background: #45b7aa; }
43
42
  .status { display: inline-block; margin-left: 15px; }
44
- .status.ok { color: #4ecdc4; }
45
- .status.error { color: #ff6b6b; }
46
- .grid { display: grid; grid-template-columns: 300px 1fr; gap: 20px; }
43
+ .status.ok { color: var(--teal); }
44
+ .status.error { color: var(--danger); }
45
+ .grid { display: grid; grid-template-columns: 320px minmax(0, 1fr); gap: 20px; }
47
46
  .sidebar {
48
- background: #16213e;
49
- border-radius: 8px;
47
+ background: var(--panel);
48
+ border-radius: 12px;
50
49
  padding: 15px;
51
50
  height: fit-content;
51
+ border: 1px solid rgba(255, 255, 255, 0.08);
52
+ }
53
+ .sidebar h3 {
54
+ color: var(--teal);
55
+ margin-bottom: 15px;
56
+ font-size: 13px;
57
+ letter-spacing: 0.08em;
58
+ text-transform: uppercase;
59
+ font-family: var(--font-heading);
52
60
  }
53
- .sidebar h3 { color: #4ecdc4; margin-bottom: 15px; font-size: 14px; }
54
61
  .conversation-list { list-style: none; }
55
62
  .conversation-list li {
56
63
  padding: 10px;
57
64
  border-radius: 4px;
58
65
  cursor: pointer;
59
66
  margin-bottom: 5px;
60
- background: #0f3460;
67
+ background: var(--panel-alt);
61
68
  }
62
69
  .conversation-list li:hover { background: #1a4a7a; }
63
- .conversation-list li.active { background: #4ecdc4; color: #1a1a2e; }
70
+ .conversation-list li.active { background: var(--teal); color: #1a1a2e; }
64
71
  .conversation-list .type { font-size: 11px; opacity: 0.7; }
65
72
  .main-panel {
66
- background: #16213e;
67
- border-radius: 8px;
73
+ background: var(--panel);
74
+ border-radius: 12px;
68
75
  padding: 20px;
69
76
  min-height: 500px;
77
+ border: 1px solid rgba(255, 255, 255, 0.08);
78
+ }
79
+ .main-panel h2 {
80
+ color: var(--teal);
81
+ margin-bottom: 15px;
82
+ font-family: var(--font-heading);
83
+ letter-spacing: -0.01em;
70
84
  }
71
- .main-panel h2 { color: #4ecdc4; margin-bottom: 15px; }
72
85
  .messages { max-height: 400px; overflow-y: auto; margin-bottom: 15px; }
73
86
  .message {
74
- background: #0f3460;
87
+ background: var(--panel-alt);
75
88
  padding: 12px;
76
89
  border-radius: 8px;
77
90
  margin-bottom: 10px;
78
91
  }
79
92
  .message .header { display: flex; justify-content: space-between; margin-bottom: 8px; }
80
- .message .user { font-weight: bold; color: #4ecdc4; }
93
+ .message .user { font-weight: bold; color: var(--teal); }
81
94
  .message .time { font-size: 12px; opacity: 0.6; }
82
95
  .message .text { line-height: 1.5; white-space: pre-wrap; }
83
96
  .send-box { display: flex; gap: 10px; }
@@ -86,17 +99,19 @@
86
99
  padding: 12px;
87
100
  border: none;
88
101
  border-radius: 4px;
89
- background: #0f3460;
102
+ background: var(--panel-alt);
90
103
  color: #fff;
104
+ min-width: 0;
91
105
  }
92
106
  .send-box button {
93
107
  padding: 12px 24px;
94
- background: #4ecdc4;
108
+ background: var(--teal);
95
109
  color: #1a1a2e;
96
110
  border: none;
97
111
  border-radius: 4px;
98
112
  cursor: pointer;
99
113
  font-weight: bold;
114
+ white-space: nowrap;
100
115
  }
101
116
  .search-section { margin-bottom: 20px; display: flex; gap: 10px; }
102
117
  .search-section input {
@@ -104,29 +119,31 @@
104
119
  padding: 10px;
105
120
  border: none;
106
121
  border-radius: 4px;
107
- background: #0f3460;
122
+ background: var(--panel-alt);
108
123
  color: #fff;
124
+ min-width: 0;
109
125
  }
110
126
  .search-section button {
111
127
  padding: 10px 20px;
112
- background: #e94560;
128
+ background: var(--search);
113
129
  color: #fff;
114
130
  border: none;
115
131
  border-radius: 4px;
116
132
  cursor: pointer;
133
+ white-space: nowrap;
117
134
  }
118
- .loading { text-align: center; padding: 40px; opacity: 0.6; }
119
- .error-msg { background: #ff6b6b22; color: #ff6b6b; padding: 15px; border-radius: 8px; }
135
+ .loading { text-align: center; padding: 40px; opacity: 0.6; color: var(--muted); }
136
+ .error-msg { background: #ff6b6b22; color: var(--danger); padding: 15px; border-radius: 8px; }
120
137
  .tabs { display: flex; gap: 5px; margin-bottom: 15px; }
121
138
  .tabs button {
122
139
  padding: 8px 16px;
123
- background: #0f3460;
140
+ background: var(--panel-alt);
124
141
  color: #fff;
125
142
  border: none;
126
143
  border-radius: 4px;
127
144
  cursor: pointer;
128
145
  }
129
- .tabs button.active { background: #4ecdc4; color: #1a1a2e; }
146
+ .tabs button.active { background: var(--teal); color: #1a1a2e; }
130
147
  /* Auth Modal */
131
148
  .modal-overlay {
132
149
  position: fixed;
@@ -139,36 +156,43 @@
139
156
  }
140
157
  .modal-overlay.hidden { display: none; }
141
158
  .modal {
142
- background: #16213e;
143
- padding: 40px;
159
+ background: var(--panel);
160
+ padding: 36px;
144
161
  border-radius: 12px;
145
- max-width: 450px;
162
+ width: min(92vw, 460px);
146
163
  text-align: center;
147
- border: 2px solid #4ecdc4;
164
+ border: 2px solid var(--teal);
165
+ max-height: calc(100vh - 40px);
166
+ overflow-y: auto;
167
+ }
168
+ .modal h2 {
169
+ color: var(--teal);
170
+ margin-bottom: 20px;
171
+ font-family: var(--font-heading);
172
+ letter-spacing: -0.01em;
148
173
  }
149
- .modal h2 { color: #4ecdc4; margin-bottom: 20px; }
150
- .modal p { color: #aaa; margin-bottom: 25px; line-height: 1.6; }
174
+ .modal p { color: #c2c7d6; margin-bottom: 25px; line-height: 1.6; }
151
175
  .modal code {
152
- background: #0f3460;
176
+ background: var(--panel-alt);
153
177
  padding: 3px 8px;
154
178
  border-radius: 4px;
155
- color: #4ecdc4;
179
+ color: var(--teal);
156
180
  }
157
181
  .modal input {
158
182
  width: 100%;
159
183
  padding: 14px;
160
- border: 2px solid #0f3460;
184
+ border: 2px solid var(--panel-alt);
161
185
  border-radius: 6px;
162
- background: #0f3460;
186
+ background: var(--panel-alt);
163
187
  color: #fff;
164
188
  font-size: 14px;
165
189
  margin-bottom: 15px;
166
190
  }
167
- .modal input:focus { border-color: #4ecdc4; outline: none; }
191
+ .modal input:focus { border-color: var(--teal); outline: none; }
168
192
  .modal button {
169
193
  width: 100%;
170
194
  padding: 14px;
171
- background: #4ecdc4;
195
+ background: var(--teal);
172
196
  color: #1a1a2e;
173
197
  border: none;
174
198
  border-radius: 6px;
@@ -176,8 +200,44 @@
176
200
  font-weight: bold;
177
201
  font-size: 16px;
178
202
  }
179
- .modal button:hover { background: #45b7aa; }
180
- .modal .error { color: #ff6b6b; margin-top: 10px; font-size: 14px; }
203
+ .modal button:hover { background: var(--teal-hover); }
204
+ .modal .error { color: var(--danger); margin-top: 10px; font-size: 14px; }
205
+
206
+ @media (max-width: 960px) {
207
+ body { padding: 16px; }
208
+ .grid { grid-template-columns: minmax(0, 1fr); }
209
+ .sidebar, .main-panel { padding: 16px; }
210
+ .messages { max-height: 50vh; }
211
+ }
212
+
213
+ @media (max-width: 640px) {
214
+ body { padding: 12px; }
215
+ h1 {
216
+ display: flex;
217
+ flex-direction: column;
218
+ gap: 8px;
219
+ }
220
+ .status {
221
+ margin-left: 0;
222
+ font-size: 14px;
223
+ }
224
+ .tabs {
225
+ display: grid;
226
+ grid-template-columns: 1fr 1fr;
227
+ }
228
+ .search-section,
229
+ .send-box {
230
+ flex-direction: column;
231
+ }
232
+ .search-section button,
233
+ .send-box button {
234
+ width: 100%;
235
+ }
236
+ .modal {
237
+ width: min(92vw, 460px);
238
+ padding: 24px 18px;
239
+ }
240
+ }
181
241
  </style>
182
242
  </head>
183
243
  <body>
@@ -198,8 +258,8 @@
198
258
  <div class="sidebar">
199
259
  <h3>CONVERSATIONS</h3>
200
260
  <div class="tabs">
201
- <button class="active" onclick="loadConversations('im,mpim')">DMs</button>
202
- <button onclick="loadConversations('public_channel,private_channel')">Channels</button>
261
+ <button class="active" onclick="loadConversations('im,mpim', this)">DMs</button>
262
+ <button onclick="loadConversations('public_channel,private_channel', this)">Channels</button>
203
263
  </div>
204
264
  <ul id="conversationList" class="conversation-list">
205
265
  <li class="loading">Enter API key to connect</li>
@@ -306,23 +366,26 @@
306
366
  }
307
367
  }
308
368
  }
309
- async function loadConversations(types) {
369
+ async function loadConversations(types, sourceButton = null) {
310
370
  const list = document.getElementById('conversationList');
311
371
  list.innerHTML = '<li class="loading">Loading...</li>';
312
372
  document.querySelectorAll('.tabs button').forEach(b => b.classList.remove('active'));
313
- if (event && event.target) event.target.classList.add('active');
373
+ if (!sourceButton) {
374
+ sourceButton = document.querySelector('.tabs button:first-child');
375
+ }
376
+ if (sourceButton) sourceButton.classList.add('active');
314
377
  try {
315
378
  const data = await api('/conversations?types=' + types);
316
379
  list.innerHTML = data.conversations.map(c =>
317
- '<li onclick="loadHistory(\'' + c.id + '\', \'' + c.name.replace(/'/g, "\\'") + '\')"><div>' + c.name + '</div><div class="type">' + c.type + '</div></li>'
380
+ '<li onclick="loadHistory(\'' + c.id + '\', \'' + c.name.replace(/'/g, "\\'") + '\', this)"><div>' + c.name + '</div><div class="type">' + c.type + '</div></li>'
318
381
  ).join('');
319
382
  } catch (e) { list.innerHTML = '<li class="error-msg">' + e.message + '</li>'; }
320
383
  }
321
- async function loadHistory(channelId, name) {
384
+ async function loadHistory(channelId, name, sourceItem = null) {
322
385
  currentChannel = channelId;
323
386
  document.getElementById('channelName').textContent = name;
324
387
  document.querySelectorAll('.conversation-list li').forEach(li => li.classList.remove('active'));
325
- event.target.closest('li').classList.add('active');
388
+ if (sourceItem) sourceItem.classList.add('active');
326
389
  const container = document.getElementById('messages');
327
390
  container.innerHTML = '<div class="loading">Loading messages...</div>';
328
391
  try {