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