@jtalk22/slack-mcp 3.0.0 → 3.2.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 +63 -28
- package/docs/SETUP.md +64 -29
- package/docs/TROUBLESHOOTING.md +28 -0
- package/lib/handlers.js +156 -0
- package/lib/slack-client.js +11 -3
- package/lib/token-store.js +6 -5
- package/lib/tools.js +131 -0
- package/package.json +35 -36
- package/public/index.html +10 -6
- package/public/share.html +128 -0
- package/scripts/setup-wizard.js +1 -1
- package/server.json +10 -4
- package/src/server-http.js +16 -1
- package/src/server.js +31 -7
- package/src/web-server.js +119 -4
- package/docs/CLOUDFLARE-BROWSER-TOOLKIT.md +0 -67
- package/docs/COMMUNICATION-STYLE.md +0 -66
- package/docs/COMPATIBILITY.md +0 -19
- package/docs/DEPLOYMENT-MODES.md +0 -55
- package/docs/HN-LAUNCH.md +0 -72
- package/docs/INDEX.md +0 -40
- package/docs/INSTALL-PROOF.md +0 -18
- package/docs/LAUNCH-COPY-v3.0.0.md +0 -73
- package/docs/LAUNCH-MATRIX.md +0 -22
- package/docs/LAUNCH-OPS.md +0 -71
- package/docs/RELEASE-HEALTH.md +0 -90
- package/docs/SUPPORT-BOUNDARIES.md +0 -49
- package/docs/USE_CASE_RECIPES.md +0 -69
- package/docs/WEB-API.md +0 -303
- package/docs/images/demo-channel-messages.png +0 -0
- package/docs/images/demo-channels.png +0 -0
- package/docs/images/demo-claude-mobile-360x800.png +0 -0
- package/docs/images/demo-claude-mobile-390x844.png +0 -0
- package/docs/images/demo-main-mobile-360x800.png +0 -0
- package/docs/images/demo-main-mobile-390x844.png +0 -0
- package/docs/images/demo-main.png +0 -0
- package/docs/images/demo-messages.png +0 -0
- package/docs/images/demo-poster.png +0 -0
- package/docs/images/demo-sidebar.png +0 -0
- package/docs/images/diagram-oauth-comparison.svg +0 -80
- package/docs/images/diagram-session-flow.svg +0 -105
- package/docs/images/web-api-mobile-360x800.png +0 -0
- package/docs/images/web-api-mobile-390x844.png +0 -0
- package/public/demo-claude.html +0 -1958
- package/public/demo-video.html +0 -235
- package/public/demo.html +0 -1196
- package/scripts/build-release-health-delta.js +0 -201
- package/scripts/capture-screenshots.js +0 -146
- package/scripts/check-owner-attribution.sh +0 -80
- package/scripts/check-public-language.sh +0 -25
- package/scripts/check-version-parity.js +0 -176
- package/scripts/cloudflare-browser-tool.js +0 -237
- package/scripts/collect-release-health.js +0 -150
- package/scripts/record-demo.js +0 -162
- package/scripts/release-preflight.js +0 -243
- package/scripts/setup-git-hooks.sh +0 -15
- package/scripts/verify-core.js +0 -159
- package/scripts/verify-install-flow.js +0 -193
- package/scripts/verify-web.js +0 -269
package/public/demo.html
DELETED
|
@@ -1,1196 +0,0 @@
|
|
|
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 - Interactive Demo</title>
|
|
7
|
-
|
|
8
|
-
<!-- Open Graph / Social Sharing -->
|
|
9
|
-
<meta property="og:type" content="website">
|
|
10
|
-
<meta property="og:url" content="https://jtalk22.github.io/slack-mcp-server/public/demo.html">
|
|
11
|
-
<meta property="og:title" content="Slack MCP Server - Session-Based Slack Access Demo">
|
|
12
|
-
<meta property="og:description" content="Session-based Slack access for Claude with your existing workspace permissions. DMs, channels, search, and threads.">
|
|
13
|
-
<meta property="og:image" content="https://raw.githubusercontent.com/jtalk22/slack-mcp-server/main/docs/images/demo-main.png">
|
|
14
|
-
|
|
15
|
-
<!-- Twitter Card -->
|
|
16
|
-
<meta name="twitter:card" content="summary_large_image">
|
|
17
|
-
<meta name="twitter:title" content="Slack MCP Server - Session-Based Slack Access Demo">
|
|
18
|
-
<meta name="twitter:description" content="Session-based Slack access for Claude with your existing workspace permissions. DMs, channels, search, and threads.">
|
|
19
|
-
<meta name="twitter:image" content="https://raw.githubusercontent.com/jtalk22/slack-mcp-server/main/docs/images/demo-main.png">
|
|
20
|
-
|
|
21
|
-
<!-- SEO -->
|
|
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
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
24
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
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
|
-
<style>
|
|
27
|
-
:root {
|
|
28
|
-
--font-heading: "Space Grotesk", "Avenir Next", "Segoe UI", sans-serif;
|
|
29
|
-
--font-body: "IBM Plex Sans", "Inter", "Segoe UI", sans-serif;
|
|
30
|
-
--bg-primary: #0f0f1a;
|
|
31
|
-
--bg-secondary: #1a1a2e;
|
|
32
|
-
--bg-tertiary: #16213e;
|
|
33
|
-
--bg-input: #0f3460;
|
|
34
|
-
--accent: #4ecdc4;
|
|
35
|
-
--accent-hover: #45b7aa;
|
|
36
|
-
--accent-glow: rgba(78, 205, 196, 0.3);
|
|
37
|
-
--danger: #e94560;
|
|
38
|
-
--claude-orange: #da7756;
|
|
39
|
-
--claude-bg: #2d2a24;
|
|
40
|
-
--text-primary: #ffffff;
|
|
41
|
-
--text-secondary: rgba(255, 255, 255, 0.7);
|
|
42
|
-
--text-muted: rgba(255, 255, 255, 0.5);
|
|
43
|
-
--border-radius: 12px;
|
|
44
|
-
--transition: all 0.2s ease;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
48
|
-
|
|
49
|
-
body {
|
|
50
|
-
font-family: var(--font-body);
|
|
51
|
-
background: var(--bg-primary);
|
|
52
|
-
color: var(--text-primary);
|
|
53
|
-
min-height: 100vh;
|
|
54
|
-
line-height: 1.5;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/* Preview Banner */
|
|
58
|
-
.preview-banner {
|
|
59
|
-
background: linear-gradient(135deg, var(--danger), #ff8c00);
|
|
60
|
-
color: white;
|
|
61
|
-
padding: 12px 20px;
|
|
62
|
-
text-align: center;
|
|
63
|
-
font-size: 14px;
|
|
64
|
-
font-weight: 500;
|
|
65
|
-
position: sticky;
|
|
66
|
-
top: 0;
|
|
67
|
-
z-index: 100;
|
|
68
|
-
}
|
|
69
|
-
.preview-banner code {
|
|
70
|
-
background: rgba(0,0,0,0.2);
|
|
71
|
-
padding: 2px 8px;
|
|
72
|
-
border-radius: 4px;
|
|
73
|
-
font-family: monospace;
|
|
74
|
-
}
|
|
75
|
-
.preview-banner a {
|
|
76
|
-
color: white;
|
|
77
|
-
text-decoration: underline;
|
|
78
|
-
}
|
|
79
|
-
.cta-strip {
|
|
80
|
-
background: rgba(15, 52, 96, 0.9);
|
|
81
|
-
border-bottom: 1px solid rgba(255, 255, 255, 0.12);
|
|
82
|
-
padding: 10px 16px;
|
|
83
|
-
display: flex;
|
|
84
|
-
justify-content: space-between;
|
|
85
|
-
align-items: center;
|
|
86
|
-
gap: 12px;
|
|
87
|
-
flex-wrap: wrap;
|
|
88
|
-
font-size: 13px;
|
|
89
|
-
}
|
|
90
|
-
.cta-links {
|
|
91
|
-
display: flex;
|
|
92
|
-
gap: 10px;
|
|
93
|
-
flex-wrap: wrap;
|
|
94
|
-
}
|
|
95
|
-
.cta-links a {
|
|
96
|
-
color: #d8efff;
|
|
97
|
-
text-decoration: none;
|
|
98
|
-
padding: 4px 8px;
|
|
99
|
-
border: 1px solid rgba(255, 255, 255, 0.25);
|
|
100
|
-
border-radius: 999px;
|
|
101
|
-
}
|
|
102
|
-
.cta-links a:hover {
|
|
103
|
-
background: rgba(255, 255, 255, 0.1);
|
|
104
|
-
}
|
|
105
|
-
.cta-note {
|
|
106
|
-
color: rgba(255, 255, 255, 0.75);
|
|
107
|
-
}
|
|
108
|
-
.cta-note a {
|
|
109
|
-
color: #9ee7ff;
|
|
110
|
-
text-decoration: underline;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/* Main Layout */
|
|
114
|
-
.split-container {
|
|
115
|
-
display: grid;
|
|
116
|
-
grid-template-columns: 420px 1fr;
|
|
117
|
-
height: calc(100vh - 88px);
|
|
118
|
-
overflow: hidden;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/* ========== LEFT PANEL: CLAUDE CHAT ========== */
|
|
122
|
-
.claude-panel {
|
|
123
|
-
background: var(--claude-bg);
|
|
124
|
-
border-right: 1px solid rgba(255,255,255,0.1);
|
|
125
|
-
display: flex;
|
|
126
|
-
flex-direction: column;
|
|
127
|
-
overflow: hidden;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
.claude-header {
|
|
131
|
-
padding: 16px 20px;
|
|
132
|
-
border-bottom: 1px solid rgba(255,255,255,0.1);
|
|
133
|
-
display: flex;
|
|
134
|
-
align-items: center;
|
|
135
|
-
gap: 12px;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
.claude-logo {
|
|
139
|
-
width: 36px;
|
|
140
|
-
height: 36px;
|
|
141
|
-
background: linear-gradient(135deg, var(--claude-orange), #c4694a);
|
|
142
|
-
border-radius: 10px;
|
|
143
|
-
display: flex;
|
|
144
|
-
align-items: center;
|
|
145
|
-
justify-content: center;
|
|
146
|
-
font-weight: 700;
|
|
147
|
-
font-size: 18px;
|
|
148
|
-
color: white;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
.claude-header h2 {
|
|
152
|
-
font-size: 16px;
|
|
153
|
-
font-weight: 600;
|
|
154
|
-
color: var(--text-primary);
|
|
155
|
-
font-family: var(--font-heading);
|
|
156
|
-
}
|
|
157
|
-
.claude-header .subtitle {
|
|
158
|
-
font-size: 12px;
|
|
159
|
-
color: var(--text-muted);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
.reset-btn {
|
|
163
|
-
margin-left: auto;
|
|
164
|
-
padding: 6px 14px;
|
|
165
|
-
background: rgba(255,255,255,0.1);
|
|
166
|
-
border: 1px solid rgba(255,255,255,0.2);
|
|
167
|
-
border-radius: 6px;
|
|
168
|
-
color: var(--text-secondary);
|
|
169
|
-
cursor: pointer;
|
|
170
|
-
font-size: 12px;
|
|
171
|
-
font-family: inherit;
|
|
172
|
-
transition: var(--transition);
|
|
173
|
-
}
|
|
174
|
-
.reset-btn:hover {
|
|
175
|
-
background: rgba(233, 69, 96, 0.2);
|
|
176
|
-
border-color: var(--danger);
|
|
177
|
-
color: var(--danger);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
.claude-chat {
|
|
181
|
-
flex: 1;
|
|
182
|
-
overflow-y: auto;
|
|
183
|
-
padding: 20px;
|
|
184
|
-
scroll-behavior: smooth;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
.chat-message {
|
|
188
|
-
margin-bottom: 20px;
|
|
189
|
-
animation: fadeIn 0.3s ease;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
@keyframes fadeIn {
|
|
193
|
-
from { opacity: 0; transform: translateY(10px); }
|
|
194
|
-
to { opacity: 1; transform: translateY(0); }
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
.chat-message.user {
|
|
198
|
-
text-align: right;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
.chat-message.user .bubble {
|
|
202
|
-
background: var(--bg-input);
|
|
203
|
-
display: inline-block;
|
|
204
|
-
text-align: left;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
.chat-message .bubble {
|
|
208
|
-
background: rgba(255,255,255,0.05);
|
|
209
|
-
padding: 14px 18px;
|
|
210
|
-
border-radius: 16px;
|
|
211
|
-
max-width: 90%;
|
|
212
|
-
font-size: 14px;
|
|
213
|
-
line-height: 1.6;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
.chat-message.claude .bubble {
|
|
217
|
-
border-top-left-radius: 4px;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
.chat-message.user .bubble {
|
|
221
|
-
border-top-right-radius: 4px;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/* Tool call styling */
|
|
225
|
-
.tool-call {
|
|
226
|
-
background: rgba(78, 205, 196, 0.1);
|
|
227
|
-
border: 1px solid rgba(78, 205, 196, 0.3);
|
|
228
|
-
border-radius: 8px;
|
|
229
|
-
padding: 12px;
|
|
230
|
-
margin: 10px 0;
|
|
231
|
-
font-family: 'SF Mono', 'Fira Code', monospace;
|
|
232
|
-
font-size: 12px;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
.tool-call .tool-name {
|
|
236
|
-
color: var(--accent);
|
|
237
|
-
font-weight: 600;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
.tool-call .tool-params {
|
|
241
|
-
color: var(--text-muted);
|
|
242
|
-
margin-top: 4px;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/* Typing indicator */
|
|
246
|
-
.typing-indicator {
|
|
247
|
-
display: flex;
|
|
248
|
-
gap: 4px;
|
|
249
|
-
padding: 8px 0;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
.typing-indicator span {
|
|
253
|
-
width: 8px;
|
|
254
|
-
height: 8px;
|
|
255
|
-
background: var(--claude-orange);
|
|
256
|
-
border-radius: 50%;
|
|
257
|
-
animation: bounce 1.4s infinite ease-in-out;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
.typing-indicator span:nth-child(1) { animation-delay: 0s; }
|
|
261
|
-
.typing-indicator span:nth-child(2) { animation-delay: 0.2s; }
|
|
262
|
-
.typing-indicator span:nth-child(3) { animation-delay: 0.4s; }
|
|
263
|
-
|
|
264
|
-
@keyframes bounce {
|
|
265
|
-
0%, 80%, 100% { transform: scale(0.6); opacity: 0.4; }
|
|
266
|
-
40% { transform: scale(1); opacity: 1; }
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/* Try It Buttons */
|
|
270
|
-
.try-buttons {
|
|
271
|
-
padding: 16px 20px;
|
|
272
|
-
border-top: 1px solid rgba(255,255,255,0.1);
|
|
273
|
-
background: rgba(0,0,0,0.2);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
.try-buttons h4 {
|
|
277
|
-
font-size: 11px;
|
|
278
|
-
text-transform: uppercase;
|
|
279
|
-
letter-spacing: 1px;
|
|
280
|
-
color: var(--text-muted);
|
|
281
|
-
margin-bottom: 12px;
|
|
282
|
-
font-family: var(--font-heading);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
.try-buttons .scenarios {
|
|
286
|
-
display: flex;
|
|
287
|
-
flex-direction: column;
|
|
288
|
-
gap: 8px;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
.scenario-btn {
|
|
292
|
-
display: flex;
|
|
293
|
-
align-items: center;
|
|
294
|
-
gap: 12px;
|
|
295
|
-
padding: 12px 16px;
|
|
296
|
-
background: rgba(255,255,255,0.05);
|
|
297
|
-
border: 1px solid rgba(255,255,255,0.1);
|
|
298
|
-
border-radius: 10px;
|
|
299
|
-
color: var(--text-primary);
|
|
300
|
-
cursor: pointer;
|
|
301
|
-
transition: var(--transition);
|
|
302
|
-
text-align: left;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
.scenario-btn:hover {
|
|
306
|
-
background: rgba(78, 205, 196, 0.1);
|
|
307
|
-
border-color: var(--accent);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
.scenario-btn:disabled {
|
|
311
|
-
opacity: 0.5;
|
|
312
|
-
cursor: not-allowed;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
.scenario-btn .icon {
|
|
316
|
-
font-size: 20px;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
.scenario-btn .text {
|
|
320
|
-
flex: 1;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
.scenario-btn .title {
|
|
324
|
-
font-weight: 600;
|
|
325
|
-
font-size: 13px;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
.scenario-btn .desc {
|
|
329
|
-
font-size: 11px;
|
|
330
|
-
color: var(--text-muted);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
/* ========== RIGHT PANEL: SLACK DASHBOARD ========== */
|
|
334
|
-
.dashboard-panel {
|
|
335
|
-
background: var(--bg-primary);
|
|
336
|
-
display: flex;
|
|
337
|
-
flex-direction: column;
|
|
338
|
-
overflow: hidden;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
.dashboard-header {
|
|
342
|
-
padding: 16px 24px;
|
|
343
|
-
border-bottom: 1px solid rgba(255,255,255,0.1);
|
|
344
|
-
display: flex;
|
|
345
|
-
align-items: center;
|
|
346
|
-
justify-content: space-between;
|
|
347
|
-
background: var(--bg-secondary);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
.dashboard-title {
|
|
351
|
-
display: flex;
|
|
352
|
-
align-items: center;
|
|
353
|
-
gap: 12px;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
.dashboard-title h2 {
|
|
357
|
-
font-size: 18px;
|
|
358
|
-
font-weight: 600;
|
|
359
|
-
font-family: var(--font-heading);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
.status-badge {
|
|
363
|
-
background: rgba(34, 197, 94, 0.2);
|
|
364
|
-
color: #22c55e;
|
|
365
|
-
padding: 4px 12px;
|
|
366
|
-
border-radius: 20px;
|
|
367
|
-
font-size: 12px;
|
|
368
|
-
font-weight: 500;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
.dashboard-content {
|
|
372
|
-
flex: 1;
|
|
373
|
-
display: grid;
|
|
374
|
-
grid-template-columns: 280px 1fr;
|
|
375
|
-
overflow: hidden;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/* Sidebar */
|
|
379
|
-
.sidebar {
|
|
380
|
-
background: var(--bg-secondary);
|
|
381
|
-
border-right: 1px solid rgba(255,255,255,0.05);
|
|
382
|
-
display: flex;
|
|
383
|
-
flex-direction: column;
|
|
384
|
-
overflow: hidden;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
.sidebar-section {
|
|
388
|
-
padding: 16px;
|
|
389
|
-
border-bottom: 1px solid rgba(255,255,255,0.05);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
.sidebar-section h3 {
|
|
393
|
-
font-size: 11px;
|
|
394
|
-
text-transform: uppercase;
|
|
395
|
-
letter-spacing: 1px;
|
|
396
|
-
color: var(--text-muted);
|
|
397
|
-
margin-bottom: 12px;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
.conversation-list {
|
|
401
|
-
list-style: none;
|
|
402
|
-
flex: 1;
|
|
403
|
-
overflow-y: auto;
|
|
404
|
-
padding: 8px;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
.conversation-item {
|
|
408
|
-
padding: 12px 14px;
|
|
409
|
-
border-radius: 8px;
|
|
410
|
-
cursor: pointer;
|
|
411
|
-
margin-bottom: 4px;
|
|
412
|
-
display: flex;
|
|
413
|
-
align-items: center;
|
|
414
|
-
gap: 12px;
|
|
415
|
-
transition: var(--transition);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
.conversation-item:hover {
|
|
419
|
-
background: rgba(255,255,255,0.05);
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
.conversation-item.active {
|
|
423
|
-
background: linear-gradient(135deg, var(--accent), #3b82f6);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
.conversation-item.active .conv-name,
|
|
427
|
-
.conversation-item.active .conv-preview {
|
|
428
|
-
color: var(--bg-primary);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
.conversation-item.highlight {
|
|
432
|
-
background: rgba(78, 205, 196, 0.2);
|
|
433
|
-
border: 1px solid var(--accent);
|
|
434
|
-
animation: pulse-highlight 1s ease-in-out;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
@keyframes pulse-highlight {
|
|
438
|
-
0%, 100% { box-shadow: 0 0 0 0 var(--accent-glow); }
|
|
439
|
-
50% { box-shadow: 0 0 20px 5px var(--accent-glow); }
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
.avatar {
|
|
443
|
-
width: 36px;
|
|
444
|
-
height: 36px;
|
|
445
|
-
border-radius: 8px;
|
|
446
|
-
display: flex;
|
|
447
|
-
align-items: center;
|
|
448
|
-
justify-content: center;
|
|
449
|
-
font-weight: 600;
|
|
450
|
-
font-size: 13px;
|
|
451
|
-
flex-shrink: 0;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
.avatar.dm { background: linear-gradient(135deg, #8b5cf6, #ec4899); }
|
|
455
|
-
.avatar.channel { background: linear-gradient(135deg, #3b82f6, #06b6d4); }
|
|
456
|
-
|
|
457
|
-
.conv-info { flex: 1; min-width: 0; }
|
|
458
|
-
.conv-name { font-weight: 500; font-size: 14px; }
|
|
459
|
-
.conv-preview { font-size: 12px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
460
|
-
|
|
461
|
-
/* Main Content Area */
|
|
462
|
-
.main-area {
|
|
463
|
-
background: var(--bg-tertiary);
|
|
464
|
-
display: flex;
|
|
465
|
-
flex-direction: column;
|
|
466
|
-
overflow: hidden;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
.main-header {
|
|
470
|
-
padding: 16px 24px;
|
|
471
|
-
border-bottom: 1px solid rgba(255,255,255,0.05);
|
|
472
|
-
display: flex;
|
|
473
|
-
align-items: center;
|
|
474
|
-
justify-content: space-between;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
.main-header h3 {
|
|
478
|
-
font-size: 16px;
|
|
479
|
-
font-weight: 600;
|
|
480
|
-
display: flex;
|
|
481
|
-
align-items: center;
|
|
482
|
-
gap: 8px;
|
|
483
|
-
font-family: var(--font-heading);
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
.online-dot {
|
|
487
|
-
width: 8px;
|
|
488
|
-
height: 8px;
|
|
489
|
-
background: #22c55e;
|
|
490
|
-
border-radius: 50%;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
.messages-container {
|
|
494
|
-
flex: 1;
|
|
495
|
-
overflow-y: auto;
|
|
496
|
-
padding: 20px 24px;
|
|
497
|
-
}
|
|
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
|
-
|
|
568
|
-
.message {
|
|
569
|
-
display: flex;
|
|
570
|
-
gap: 14px;
|
|
571
|
-
padding: 12px;
|
|
572
|
-
border-radius: 10px;
|
|
573
|
-
margin-bottom: 8px;
|
|
574
|
-
transition: var(--transition);
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
.message:hover {
|
|
578
|
-
background: rgba(255,255,255,0.03);
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
.message.highlight {
|
|
582
|
-
background: rgba(78, 205, 196, 0.15);
|
|
583
|
-
border: 1px solid rgba(78, 205, 196, 0.4);
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
.message-avatar {
|
|
587
|
-
width: 40px;
|
|
588
|
-
height: 40px;
|
|
589
|
-
border-radius: 10px;
|
|
590
|
-
display: flex;
|
|
591
|
-
align-items: center;
|
|
592
|
-
justify-content: center;
|
|
593
|
-
font-weight: 600;
|
|
594
|
-
flex-shrink: 0;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
.message-content { flex: 1; }
|
|
598
|
-
.message-header { display: flex; align-items: baseline; gap: 10px; margin-bottom: 4px; }
|
|
599
|
-
.message-user { font-weight: 600; font-size: 14px; color: var(--accent); }
|
|
600
|
-
.message-time { font-size: 12px; color: var(--text-muted); }
|
|
601
|
-
.message-text { font-size: 14px; color: var(--text-secondary); line-height: 1.5; }
|
|
602
|
-
|
|
603
|
-
.message-text code {
|
|
604
|
-
background: rgba(0,0,0,0.3);
|
|
605
|
-
padding: 2px 6px;
|
|
606
|
-
border-radius: 4px;
|
|
607
|
-
color: #f472b6;
|
|
608
|
-
font-family: monospace;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
/* User Profile Card */
|
|
612
|
-
.user-card {
|
|
613
|
-
background: var(--bg-secondary);
|
|
614
|
-
border-radius: 12px;
|
|
615
|
-
padding: 24px;
|
|
616
|
-
text-align: center;
|
|
617
|
-
max-width: 300px;
|
|
618
|
-
margin: 40px auto;
|
|
619
|
-
border: 1px solid rgba(255,255,255,0.1);
|
|
620
|
-
display: none;
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
.user-card.visible {
|
|
624
|
-
display: block;
|
|
625
|
-
animation: fadeIn 0.4s ease;
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
.user-card .profile-avatar {
|
|
629
|
-
width: 80px;
|
|
630
|
-
height: 80px;
|
|
631
|
-
border-radius: 20px;
|
|
632
|
-
margin: 0 auto 16px;
|
|
633
|
-
display: flex;
|
|
634
|
-
align-items: center;
|
|
635
|
-
justify-content: center;
|
|
636
|
-
font-size: 32px;
|
|
637
|
-
font-weight: 700;
|
|
638
|
-
background: linear-gradient(135deg, #8b5cf6, #ec4899);
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
.user-card h4 {
|
|
642
|
-
font-size: 20px;
|
|
643
|
-
margin-bottom: 4px;
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
.user-card .title {
|
|
647
|
-
color: var(--text-muted);
|
|
648
|
-
font-size: 14px;
|
|
649
|
-
margin-bottom: 16px;
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
.user-card .stats {
|
|
653
|
-
display: flex;
|
|
654
|
-
justify-content: center;
|
|
655
|
-
gap: 24px;
|
|
656
|
-
padding-top: 16px;
|
|
657
|
-
border-top: 1px solid rgba(255,255,255,0.1);
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
.user-card .stat {
|
|
661
|
-
text-align: center;
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
.user-card .stat-value {
|
|
665
|
-
font-size: 20px;
|
|
666
|
-
font-weight: 700;
|
|
667
|
-
color: var(--accent);
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
.user-card .stat-label {
|
|
671
|
-
font-size: 11px;
|
|
672
|
-
color: var(--text-muted);
|
|
673
|
-
text-transform: uppercase;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
/* Empty State */
|
|
677
|
-
.empty-state {
|
|
678
|
-
text-align: center;
|
|
679
|
-
padding: 60px 20px;
|
|
680
|
-
color: var(--text-muted);
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
.empty-state .icon {
|
|
684
|
-
font-size: 48px;
|
|
685
|
-
margin-bottom: 16px;
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
/* Mobile */
|
|
689
|
-
@media (max-width: 1000px) {
|
|
690
|
-
.split-container {
|
|
691
|
-
grid-template-columns: 1fr;
|
|
692
|
-
height: auto;
|
|
693
|
-
min-height: calc(100vh - 88px);
|
|
694
|
-
}
|
|
695
|
-
.claude-panel {
|
|
696
|
-
display: none;
|
|
697
|
-
}
|
|
698
|
-
.mobile-scenario-rail {
|
|
699
|
-
display: block;
|
|
700
|
-
}
|
|
701
|
-
.dashboard-content {
|
|
702
|
-
grid-template-columns: 1fr;
|
|
703
|
-
}
|
|
704
|
-
.sidebar {
|
|
705
|
-
display: none;
|
|
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
|
-
}
|
|
739
|
-
}
|
|
740
|
-
</style>
|
|
741
|
-
</head>
|
|
742
|
-
<body>
|
|
743
|
-
<!-- Preview Banner -->
|
|
744
|
-
<div class="preview-banner">
|
|
745
|
-
STATIC PREVIEW - No real data. Run <code>npm run web</code> for live dashboard.
|
|
746
|
-
<a href="https://github.com/jtalk22/slack-mcp-server">View on GitHub</a>
|
|
747
|
-
</div>
|
|
748
|
-
<div class="cta-strip">
|
|
749
|
-
<div class="cta-links">
|
|
750
|
-
<a href="https://www.npmjs.com/package/@jtalk22/slack-mcp" target="_blank" rel="noopener noreferrer">Install</a>
|
|
751
|
-
<a href="https://github.com/jtalk22/slack-mcp-server/blob/main/docs/SETUP.md" target="_blank" rel="noopener noreferrer">Setup Guide</a>
|
|
752
|
-
<a href="https://github.com/jtalk22/slack-mcp-server#30-second-compatibility-check" target="_blank" rel="noopener noreferrer">30-Second Check</a>
|
|
753
|
-
</div>
|
|
754
|
-
<div class="cta-note">
|
|
755
|
-
Free local-first path with team rollout references in <a href="https://github.com/jtalk22/slack-mcp-server/blob/main/docs/DEPLOYMENT-MODES.md" target="_blank" rel="noopener noreferrer">Deployment Modes</a>.
|
|
756
|
-
</div>
|
|
757
|
-
</div>
|
|
758
|
-
|
|
759
|
-
<div class="split-container">
|
|
760
|
-
<!-- LEFT: Claude Chat Panel -->
|
|
761
|
-
<div class="claude-panel">
|
|
762
|
-
<div class="claude-header">
|
|
763
|
-
<div class="claude-logo">C</div>
|
|
764
|
-
<div>
|
|
765
|
-
<h2>Claude</h2>
|
|
766
|
-
<div class="subtitle">with Slack MCP tools</div>
|
|
767
|
-
</div>
|
|
768
|
-
<button class="reset-btn" onclick="resetDemo()">Reset Demo</button>
|
|
769
|
-
</div>
|
|
770
|
-
|
|
771
|
-
<div class="claude-chat" id="claudeChat">
|
|
772
|
-
<div class="chat-message claude">
|
|
773
|
-
<div class="bubble">
|
|
774
|
-
Hi! I have access to your Slack workspace through MCP tools. I can search messages, list conversations, look up users, and more.
|
|
775
|
-
<br><br>
|
|
776
|
-
Try one of the scenarios below to see how it works!
|
|
777
|
-
</div>
|
|
778
|
-
</div>
|
|
779
|
-
</div>
|
|
780
|
-
|
|
781
|
-
<div class="try-buttons">
|
|
782
|
-
<h4>Try a Scenario</h4>
|
|
783
|
-
<div class="scenarios">
|
|
784
|
-
<button class="scenario-btn" onclick="runScenario('findKey')" id="btn-findKey">
|
|
785
|
-
<span class="icon">🔑</span>
|
|
786
|
-
<div class="text">
|
|
787
|
-
<div class="title">Find the API Key</div>
|
|
788
|
-
<div class="desc">Search DMs for sensitive information</div>
|
|
789
|
-
</div>
|
|
790
|
-
</button>
|
|
791
|
-
<button class="scenario-btn" onclick="runScenario('listChannels')" id="btn-listChannels">
|
|
792
|
-
<span class="icon">📋</span>
|
|
793
|
-
<div class="text">
|
|
794
|
-
<div class="title">List Channels</div>
|
|
795
|
-
<div class="desc">Get all workspace channels</div>
|
|
796
|
-
</div>
|
|
797
|
-
</button>
|
|
798
|
-
<button class="scenario-btn" onclick="runScenario('whoIsAlex')" id="btn-whoIsAlex">
|
|
799
|
-
<span class="icon">👤</span>
|
|
800
|
-
<div class="text">
|
|
801
|
-
<div class="title">Who is Alex?</div>
|
|
802
|
-
<div class="desc">Look up user profile and activity</div>
|
|
803
|
-
</div>
|
|
804
|
-
</button>
|
|
805
|
-
</div>
|
|
806
|
-
</div>
|
|
807
|
-
</div>
|
|
808
|
-
|
|
809
|
-
<!-- RIGHT: Slack Dashboard -->
|
|
810
|
-
<div class="dashboard-panel">
|
|
811
|
-
<div class="dashboard-header">
|
|
812
|
-
<div class="dashboard-title">
|
|
813
|
-
<h2>Slack Dashboard</h2>
|
|
814
|
-
<span class="status-badge">Connected</span>
|
|
815
|
-
</div>
|
|
816
|
-
<div style="color: var(--text-muted); font-size: 13px;">
|
|
817
|
-
alex.chen @ Acme Corp
|
|
818
|
-
</div>
|
|
819
|
-
</div>
|
|
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
|
-
|
|
843
|
-
<div class="dashboard-content">
|
|
844
|
-
<aside class="sidebar">
|
|
845
|
-
<div class="sidebar-section">
|
|
846
|
-
<h3>Conversations</h3>
|
|
847
|
-
</div>
|
|
848
|
-
<ul class="conversation-list" id="conversationList">
|
|
849
|
-
<!-- Populated by JS -->
|
|
850
|
-
</ul>
|
|
851
|
-
</aside>
|
|
852
|
-
|
|
853
|
-
<main class="main-area">
|
|
854
|
-
<div class="main-header">
|
|
855
|
-
<h3 id="mainTitle"><span class="online-dot"></span> Select a conversation</h3>
|
|
856
|
-
</div>
|
|
857
|
-
<div class="messages-container" id="messagesContainer">
|
|
858
|
-
<div class="empty-state">
|
|
859
|
-
<div class="icon">💬</div>
|
|
860
|
-
<div>Click a scenario to see Claude interact with your Slack data</div>
|
|
861
|
-
</div>
|
|
862
|
-
</div>
|
|
863
|
-
</main>
|
|
864
|
-
</div>
|
|
865
|
-
</div>
|
|
866
|
-
</div>
|
|
867
|
-
|
|
868
|
-
<script>
|
|
869
|
-
// ========== MOCK DATA ==========
|
|
870
|
-
const mockData = {
|
|
871
|
-
dms: [
|
|
872
|
-
{ id: 'D001', name: 'Sarah Johnson', initials: 'SJ', preview: 'The API key is sk-abc123...', type: 'dm' },
|
|
873
|
-
{ id: 'D002', name: 'Mike Chen', initials: 'MC', preview: 'Are we still on for 2pm?', type: 'dm' },
|
|
874
|
-
{ id: 'D003', name: 'Alex Chen', initials: 'AC', preview: 'I pushed the fix to main', type: 'dm' },
|
|
875
|
-
{ id: 'D004', name: 'Emily Davis', initials: 'ED', preview: 'Thanks for the review!', type: 'dm' },
|
|
876
|
-
],
|
|
877
|
-
channels: [
|
|
878
|
-
{ id: 'C001', name: '#engineering', initials: '#', preview: 'Deploy complete', type: 'channel' },
|
|
879
|
-
{ id: 'C002', name: '#product', initials: '#', preview: 'Q4 roadmap finalized', type: 'channel' },
|
|
880
|
-
{ id: 'C003', name: '#random', initials: '#', preview: 'Anyone up for lunch?', type: 'channel' },
|
|
881
|
-
{ id: 'C004', name: '#incidents', initials: '#', preview: 'All clear', type: 'channel' },
|
|
882
|
-
{ id: 'C005', name: '#design', initials: '#', preview: 'New mockups ready', type: 'channel' },
|
|
883
|
-
],
|
|
884
|
-
messages: {
|
|
885
|
-
'D001': [
|
|
886
|
-
{ user: 'Sarah Johnson', initials: 'SJ', time: '2:32 PM', text: 'Hey, can you send me the API key for the staging environment?' },
|
|
887
|
-
{ user: 'You', initials: 'AC', time: '2:35 PM', text: 'Sure, one sec...' },
|
|
888
|
-
{ user: 'You', initials: 'AC', time: '2:36 PM', text: 'Here it is: <code>sk-abc123-staging-key-xyz789</code>', highlight: true },
|
|
889
|
-
{ user: 'Sarah Johnson', initials: 'SJ', time: '2:37 PM', text: 'Got it, thanks!' },
|
|
890
|
-
],
|
|
891
|
-
'D003': [
|
|
892
|
-
{ user: 'Alex Chen', initials: 'AC', time: '11:20 AM', text: 'I pushed the fix to main' },
|
|
893
|
-
{ user: 'You', initials: 'YO', time: '11:22 AM', text: 'Nice! I\'ll review it after lunch' },
|
|
894
|
-
{ user: 'Alex Chen', initials: 'AC', time: '11:23 AM', text: 'Sounds good. Let me know if you have questions' },
|
|
895
|
-
]
|
|
896
|
-
},
|
|
897
|
-
users: {
|
|
898
|
-
'alex': {
|
|
899
|
-
name: 'Alex Chen',
|
|
900
|
-
initials: 'AC',
|
|
901
|
-
title: 'Senior Engineer',
|
|
902
|
-
messages: 847,
|
|
903
|
-
channels: 12
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
};
|
|
907
|
-
|
|
908
|
-
let isRunning = false;
|
|
909
|
-
|
|
910
|
-
// ========== RENDERING ==========
|
|
911
|
-
function renderConversations(list, highlightId = null) {
|
|
912
|
-
const container = document.getElementById('conversationList');
|
|
913
|
-
container.innerHTML = list.map(c => `
|
|
914
|
-
<li class="conversation-item ${c.id === highlightId ? 'highlight active' : ''}" data-id="${c.id}">
|
|
915
|
-
<div class="avatar ${c.type}">${c.initials}</div>
|
|
916
|
-
<div class="conv-info">
|
|
917
|
-
<div class="conv-name">${c.name}</div>
|
|
918
|
-
<div class="conv-preview">${c.preview}</div>
|
|
919
|
-
</div>
|
|
920
|
-
</li>
|
|
921
|
-
`).join('');
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
function renderMessages(channelId, highlightIndex = -1) {
|
|
925
|
-
const container = document.getElementById('messagesContainer');
|
|
926
|
-
const messages = mockData.messages[channelId] || [];
|
|
927
|
-
|
|
928
|
-
if (messages.length === 0) {
|
|
929
|
-
container.innerHTML = '<div class="empty-state"><div class="icon">💬</div><div>No messages</div></div>';
|
|
930
|
-
return;
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
container.innerHTML = messages.map((m, i) => `
|
|
934
|
-
<div class="message ${m.highlight || i === highlightIndex ? 'highlight' : ''}">
|
|
935
|
-
<div class="message-avatar" style="background: linear-gradient(135deg, ${getColor(m.initials)})">${m.initials}</div>
|
|
936
|
-
<div class="message-content">
|
|
937
|
-
<div class="message-header">
|
|
938
|
-
<span class="message-user">${m.user}</span>
|
|
939
|
-
<span class="message-time">${m.time}</span>
|
|
940
|
-
</div>
|
|
941
|
-
<div class="message-text">${m.text}</div>
|
|
942
|
-
</div>
|
|
943
|
-
</div>
|
|
944
|
-
`).join('');
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
function showUserCard(userId) {
|
|
948
|
-
const user = mockData.users[userId];
|
|
949
|
-
if (!user) return;
|
|
950
|
-
|
|
951
|
-
// Render the user card directly into the container
|
|
952
|
-
document.getElementById('messagesContainer').innerHTML = `
|
|
953
|
-
<div class="user-card visible">
|
|
954
|
-
<div class="profile-avatar">${user.initials}</div>
|
|
955
|
-
<h4>${user.name}</h4>
|
|
956
|
-
<div class="title">${user.title}</div>
|
|
957
|
-
<div class="stats">
|
|
958
|
-
<div class="stat">
|
|
959
|
-
<div class="stat-value">${user.messages}</div>
|
|
960
|
-
<div class="stat-label">Messages</div>
|
|
961
|
-
</div>
|
|
962
|
-
<div class="stat">
|
|
963
|
-
<div class="stat-value">${user.channels}</div>
|
|
964
|
-
<div class="stat-label">Channels</div>
|
|
965
|
-
</div>
|
|
966
|
-
</div>
|
|
967
|
-
</div>
|
|
968
|
-
`;
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
function getColor(initials) {
|
|
972
|
-
const colors = [
|
|
973
|
-
'#8b5cf6, #ec4899',
|
|
974
|
-
'#3b82f6, #06b6d4',
|
|
975
|
-
'#f59e0b, #ef4444',
|
|
976
|
-
'#10b981, #3b82f6'
|
|
977
|
-
];
|
|
978
|
-
return colors[initials.charCodeAt(0) % colors.length];
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
// ========== CLAUDE CHAT ==========
|
|
982
|
-
function addUserMessage(text) {
|
|
983
|
-
const chat = document.getElementById('claudeChat');
|
|
984
|
-
chat.innerHTML += `
|
|
985
|
-
<div class="chat-message user">
|
|
986
|
-
<div class="bubble">${text}</div>
|
|
987
|
-
</div>
|
|
988
|
-
`;
|
|
989
|
-
chat.scrollTop = chat.scrollHeight;
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
function addClaudeMessage(html) {
|
|
993
|
-
const chat = document.getElementById('claudeChat');
|
|
994
|
-
chat.innerHTML += `
|
|
995
|
-
<div class="chat-message claude">
|
|
996
|
-
<div class="bubble">${html}</div>
|
|
997
|
-
</div>
|
|
998
|
-
`;
|
|
999
|
-
chat.scrollTop = chat.scrollHeight;
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
function addToolCall(toolName, params) {
|
|
1003
|
-
const chat = document.getElementById('claudeChat');
|
|
1004
|
-
chat.innerHTML += `
|
|
1005
|
-
<div class="chat-message claude">
|
|
1006
|
-
<div class="bubble">
|
|
1007
|
-
<div class="tool-call">
|
|
1008
|
-
<div class="tool-name">${toolName}</div>
|
|
1009
|
-
<div class="tool-params">${params}</div>
|
|
1010
|
-
</div>
|
|
1011
|
-
</div>
|
|
1012
|
-
</div>
|
|
1013
|
-
`;
|
|
1014
|
-
chat.scrollTop = chat.scrollHeight;
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
function showTyping() {
|
|
1018
|
-
const chat = document.getElementById('claudeChat');
|
|
1019
|
-
const id = 'typing-' + Date.now();
|
|
1020
|
-
chat.innerHTML += `
|
|
1021
|
-
<div class="chat-message claude" id="${id}">
|
|
1022
|
-
<div class="bubble">
|
|
1023
|
-
<div class="typing-indicator">
|
|
1024
|
-
<span></span><span></span><span></span>
|
|
1025
|
-
</div>
|
|
1026
|
-
</div>
|
|
1027
|
-
</div>
|
|
1028
|
-
`;
|
|
1029
|
-
chat.scrollTop = chat.scrollHeight;
|
|
1030
|
-
return id;
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
function removeTyping(id) {
|
|
1034
|
-
const el = document.getElementById(id);
|
|
1035
|
-
if (el) el.remove();
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
// ========== SCENARIOS ==========
|
|
1039
|
-
async function runScenario(scenario) {
|
|
1040
|
-
if (isRunning) return;
|
|
1041
|
-
isRunning = true;
|
|
1042
|
-
|
|
1043
|
-
// Disable all buttons
|
|
1044
|
-
document.querySelectorAll('.scenario-btn').forEach(btn => btn.disabled = true);
|
|
1045
|
-
|
|
1046
|
-
switch (scenario) {
|
|
1047
|
-
case 'findKey':
|
|
1048
|
-
await scenarioFindKey();
|
|
1049
|
-
break;
|
|
1050
|
-
case 'listChannels':
|
|
1051
|
-
await scenarioListChannels();
|
|
1052
|
-
break;
|
|
1053
|
-
case 'whoIsAlex':
|
|
1054
|
-
await scenarioWhoIsAlex();
|
|
1055
|
-
break;
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
// Re-enable buttons
|
|
1059
|
-
document.querySelectorAll('.scenario-btn').forEach(btn => btn.disabled = false);
|
|
1060
|
-
isRunning = false;
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
async function scenarioFindKey() {
|
|
1064
|
-
// User asks
|
|
1065
|
-
addUserMessage("Can you find the API key that Sarah sent me?");
|
|
1066
|
-
await delay(500);
|
|
1067
|
-
|
|
1068
|
-
// Claude thinks
|
|
1069
|
-
const typing1 = showTyping();
|
|
1070
|
-
await delay(600);
|
|
1071
|
-
removeTyping(typing1);
|
|
1072
|
-
|
|
1073
|
-
// Tool call
|
|
1074
|
-
addToolCall('slack_search_messages', '{ query: "API key", count: 10 }');
|
|
1075
|
-
await delay(900);
|
|
1076
|
-
|
|
1077
|
-
// Dashboard updates - show DMs with Sarah highlighted
|
|
1078
|
-
renderConversations(mockData.dms, 'D001');
|
|
1079
|
-
document.getElementById('mainTitle').innerHTML = '<span class="online-dot"></span> Sarah Johnson';
|
|
1080
|
-
await delay(300);
|
|
1081
|
-
|
|
1082
|
-
// Show messages with the key highlighted
|
|
1083
|
-
renderMessages('D001');
|
|
1084
|
-
await delay(600);
|
|
1085
|
-
|
|
1086
|
-
// Claude responds
|
|
1087
|
-
const typing2 = showTyping();
|
|
1088
|
-
await delay(500);
|
|
1089
|
-
removeTyping(typing2);
|
|
1090
|
-
|
|
1091
|
-
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.`);
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
async function scenarioListChannels() {
|
|
1095
|
-
addUserMessage("What channels am I in?");
|
|
1096
|
-
await delay(500);
|
|
1097
|
-
|
|
1098
|
-
const typing1 = showTyping();
|
|
1099
|
-
await delay(600);
|
|
1100
|
-
removeTyping(typing1);
|
|
1101
|
-
|
|
1102
|
-
addToolCall('slack_list_conversations', '{ types: "public_channel,private_channel" }');
|
|
1103
|
-
await delay(900);
|
|
1104
|
-
|
|
1105
|
-
// Dashboard updates to show channels
|
|
1106
|
-
renderConversations(mockData.channels);
|
|
1107
|
-
document.getElementById('mainTitle').innerHTML = 'Channels';
|
|
1108
|
-
document.getElementById('messagesContainer').innerHTML = `
|
|
1109
|
-
<div class="empty-state">
|
|
1110
|
-
<div class="icon">📋</div>
|
|
1111
|
-
<div>5 channels loaded</div>
|
|
1112
|
-
</div>
|
|
1113
|
-
`;
|
|
1114
|
-
await delay(500);
|
|
1115
|
-
|
|
1116
|
-
const typing2 = showTyping();
|
|
1117
|
-
await delay(600);
|
|
1118
|
-
removeTyping(typing2);
|
|
1119
|
-
|
|
1120
|
-
addClaudeMessage(`You're a member of <strong>5 channels</strong>:<br><br>
|
|
1121
|
-
• <strong>#engineering</strong> - Latest: "Deploy complete"<br>
|
|
1122
|
-
• <strong>#product</strong> - Q4 roadmap discussions<br>
|
|
1123
|
-
• <strong>#random</strong> - Social chat<br>
|
|
1124
|
-
• <strong>#incidents</strong> - On-call alerts<br>
|
|
1125
|
-
• <strong>#design</strong> - Design team updates<br><br>
|
|
1126
|
-
The most active appears to be #engineering.`);
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
async function scenarioWhoIsAlex() {
|
|
1130
|
-
addUserMessage("Who is Alex? What does he do here?");
|
|
1131
|
-
await delay(500);
|
|
1132
|
-
|
|
1133
|
-
const typing1 = showTyping();
|
|
1134
|
-
await delay(600);
|
|
1135
|
-
removeTyping(typing1);
|
|
1136
|
-
|
|
1137
|
-
addToolCall('slack_users_info', '{ user_id: "U_ALEX" }');
|
|
1138
|
-
await delay(600);
|
|
1139
|
-
|
|
1140
|
-
// Highlight Alex in DMs
|
|
1141
|
-
renderConversations(mockData.dms, 'D003');
|
|
1142
|
-
await delay(300);
|
|
1143
|
-
|
|
1144
|
-
addToolCall('slack_search_messages', '{ query: "from:alex", count: 50 }');
|
|
1145
|
-
await delay(900);
|
|
1146
|
-
|
|
1147
|
-
// Show user profile card
|
|
1148
|
-
document.getElementById('mainTitle').innerHTML = '<span class="online-dot"></span> User Profile';
|
|
1149
|
-
showUserCard('alex');
|
|
1150
|
-
await delay(500);
|
|
1151
|
-
|
|
1152
|
-
const typing2 = showTyping();
|
|
1153
|
-
await delay(700);
|
|
1154
|
-
removeTyping(typing2);
|
|
1155
|
-
|
|
1156
|
-
addClaudeMessage(`<strong>Alex Chen</strong> is a Senior Engineer on your team.<br><br>
|
|
1157
|
-
📊 <strong>Activity:</strong> 847 messages across 12 channels<br>
|
|
1158
|
-
💬 <strong>Most active in:</strong> #engineering, #incidents<br>
|
|
1159
|
-
🕐 <strong>Recent:</strong> Pushed a fix to main branch today<br><br>
|
|
1160
|
-
Based on his messages, he focuses on backend infrastructure and is often involved in incident response.`);
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
|
-
function delay(ms) {
|
|
1164
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
function resetDemo() {
|
|
1168
|
-
// Reset chat to initial state
|
|
1169
|
-
document.getElementById('claudeChat').innerHTML = `
|
|
1170
|
-
<div class="chat-message claude">
|
|
1171
|
-
<div class="bubble">
|
|
1172
|
-
Hi! I have access to your Slack workspace through MCP tools. I can search messages, list conversations, look up users, and more.
|
|
1173
|
-
<br><br>
|
|
1174
|
-
Try one of the scenarios below to see how it works!
|
|
1175
|
-
</div>
|
|
1176
|
-
</div>
|
|
1177
|
-
`;
|
|
1178
|
-
|
|
1179
|
-
// Reset dashboard
|
|
1180
|
-
renderConversations(mockData.dms);
|
|
1181
|
-
document.getElementById('mainTitle').innerHTML = '<span class="online-dot"></span> Select a conversation';
|
|
1182
|
-
document.getElementById('messagesContainer').innerHTML = `
|
|
1183
|
-
<div class="empty-state">
|
|
1184
|
-
<div class="icon">💬</div>
|
|
1185
|
-
<div>Click a scenario to see Claude interact with your Slack data</div>
|
|
1186
|
-
</div>
|
|
1187
|
-
`;
|
|
1188
|
-
|
|
1189
|
-
isRunning = false;
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
// ========== INIT ==========
|
|
1193
|
-
renderConversations(mockData.dms);
|
|
1194
|
-
</script>
|
|
1195
|
-
</body>
|
|
1196
|
-
</html>
|