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