559 lines
17 KiB
HTML
559 lines
17 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>MCP Chat Simulation</title>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
|
|
:root {
|
|
--bg-primary: #0f0f1a;
|
|
--bg-secondary: #1a1a2e;
|
|
--bg-tertiary: #252542;
|
|
--border: #2a2a4a;
|
|
--input-bg: #12121f;
|
|
--text: #e2e2f0;
|
|
--text-muted: #8888aa;
|
|
--purple: #6366f1;
|
|
--purple-end: #8b5cf6;
|
|
--stripe: #635BFF;
|
|
--green: #22c55e;
|
|
}
|
|
|
|
body {
|
|
font-family: 'Inter', -apple-system, sans-serif;
|
|
background: var(--bg-primary);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.simulation-container {
|
|
width: 100vw;
|
|
height: 100vh;
|
|
overflow: hidden;
|
|
position: relative;
|
|
background:
|
|
radial-gradient(ellipse at 30% 20%, rgba(99, 91, 255, 0.08) 0%, transparent 50%),
|
|
radial-gradient(ellipse at 70% 80%, rgba(118, 75, 162, 0.08) 0%, transparent 50%),
|
|
var(--bg-primary);
|
|
}
|
|
|
|
.canvas {
|
|
position: absolute;
|
|
width: 2000px;
|
|
height: 1400px;
|
|
transform-origin: top left;
|
|
will-change: transform;
|
|
}
|
|
|
|
.chat-window {
|
|
position: absolute;
|
|
left: 250px;
|
|
top: 150px;
|
|
width: 1400px;
|
|
height: 1000px;
|
|
background: var(--bg-secondary);
|
|
border-radius: 20px;
|
|
box-shadow: 0 25px 80px rgba(0,0,0,0.5);
|
|
border: 1px solid var(--border);
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.titlebar {
|
|
height: 52px;
|
|
background: var(--bg-tertiary);
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0 20px;
|
|
border-bottom: 1px solid var(--border);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.traffic-lights { display: flex; gap: 8px; }
|
|
.light { width: 12px; height: 12px; border-radius: 50%; }
|
|
.light.red { background: #ff5f57; }
|
|
.light.yellow { background: #ffbd2e; }
|
|
.light.green { background: #27ca40; }
|
|
|
|
.title { flex: 1; text-align: center; color: var(--text-muted); font-size: 14px; }
|
|
|
|
.messages {
|
|
flex: 1;
|
|
padding: 28px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 20px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.message {
|
|
max-width: 70%;
|
|
padding: 16px 22px;
|
|
border-radius: 18px;
|
|
font-size: 17px;
|
|
line-height: 1.5;
|
|
opacity: 0;
|
|
transform: translateY(15px);
|
|
transition: all 0.5s cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
|
|
.message.visible {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
|
|
.message.user {
|
|
align-self: flex-end;
|
|
background: linear-gradient(135deg, var(--purple), var(--purple-end));
|
|
color: var(--text);
|
|
border-bottom-right-radius: 6px;
|
|
}
|
|
|
|
.message.ai {
|
|
align-self: flex-start;
|
|
background: var(--border);
|
|
color: var(--text);
|
|
border-bottom-left-radius: 6px;
|
|
}
|
|
|
|
.message.insight {
|
|
background: linear-gradient(135deg, var(--green), #16a34a);
|
|
box-shadow: 0 4px 20px rgba(34, 197, 94, 0.3);
|
|
}
|
|
|
|
.cursor {
|
|
display: inline-block;
|
|
width: 2px;
|
|
height: 1em;
|
|
background: var(--purple);
|
|
margin-left: 2px;
|
|
animation: blink 0.7s infinite;
|
|
}
|
|
|
|
@keyframes blink {
|
|
0%, 50% { opacity: 1; }
|
|
51%, 100% { opacity: 0; }
|
|
}
|
|
|
|
.typing-indicator {
|
|
display: flex;
|
|
gap: 5px;
|
|
padding: 16px 22px;
|
|
background: var(--border);
|
|
border-radius: 18px;
|
|
border-bottom-left-radius: 6px;
|
|
align-self: flex-start;
|
|
opacity: 0;
|
|
transition: opacity 0.3s;
|
|
}
|
|
|
|
.typing-indicator.visible { opacity: 1; }
|
|
|
|
.typing-dot {
|
|
width: 8px;
|
|
height: 8px;
|
|
background: var(--purple);
|
|
border-radius: 50%;
|
|
animation: bounce 1.4s infinite ease-in-out;
|
|
}
|
|
|
|
.typing-dot:nth-child(2) { animation-delay: 0.2s; }
|
|
.typing-dot:nth-child(3) { animation-delay: 0.4s; }
|
|
|
|
@keyframes bounce {
|
|
0%, 80%, 100% { transform: translateY(0); }
|
|
40% { transform: translateY(-6px); }
|
|
}
|
|
|
|
.spinner {
|
|
display: inline-block;
|
|
width: 18px;
|
|
height: 18px;
|
|
border: 2px solid var(--border);
|
|
border-top-color: var(--stripe);
|
|
border-radius: 50%;
|
|
animation: spin 0.8s linear infinite;
|
|
margin-right: 10px;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
|
|
.ai-response-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
align-self: flex-start;
|
|
max-width: 80%;
|
|
}
|
|
|
|
.embed-wrapper {
|
|
opacity: 0;
|
|
transform: scale(0.85);
|
|
transform-origin: top left;
|
|
transition: all 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
}
|
|
|
|
.embed-wrapper.visible {
|
|
opacity: 1;
|
|
transform: scale(1);
|
|
}
|
|
|
|
.stripe-embed {
|
|
background: #fff;
|
|
border-radius: 12px;
|
|
overflow: hidden;
|
|
width: 480px;
|
|
box-shadow: 0 6px 30px rgba(99, 91, 255, 0.2);
|
|
}
|
|
|
|
.embed-header {
|
|
padding: 14px 18px;
|
|
border-bottom: 1px solid #E3E8EE;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
.stripe-logo {
|
|
background: var(--stripe);
|
|
color: white;
|
|
font-weight: 700;
|
|
font-size: 11px;
|
|
padding: 5px 10px;
|
|
border-radius: 5px;
|
|
}
|
|
|
|
.embed-title { font-size: 14px; font-weight: 600; color: #1A1F36; }
|
|
.embed-subtitle { font-size: 10px; color: #697386; }
|
|
|
|
.embed-stats {
|
|
display: flex;
|
|
gap: 24px;
|
|
padding: 12px 18px;
|
|
background: #F6F9FC;
|
|
border-bottom: 1px solid #E3E8EE;
|
|
}
|
|
|
|
.stat-value { font-size: 18px; font-weight: 600; color: #30B566; }
|
|
.stat-label { font-size: 9px; color: #697386; text-transform: uppercase; letter-spacing: 0.5px; }
|
|
|
|
.embed-row {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 10px 18px;
|
|
border-bottom: 1px solid #E3E8EE;
|
|
opacity: 0;
|
|
transform: translateX(-10px);
|
|
transition: all 0.35s ease;
|
|
}
|
|
|
|
.embed-row.visible {
|
|
opacity: 1;
|
|
transform: translateX(0);
|
|
}
|
|
|
|
.embed-row:last-child { border-bottom: none; }
|
|
|
|
.row-email { flex: 1; font-size: 12px; color: #1A1F36; font-weight: 500; }
|
|
.row-amount { font-size: 13px; font-weight: 600; color: #1A1F36; margin-right: 14px; }
|
|
.row-status {
|
|
padding: 3px 8px;
|
|
border-radius: 4px;
|
|
font-size: 10px;
|
|
font-weight: 500;
|
|
}
|
|
.row-status.pending { background: #FFF8E6; color: #9C6F19; }
|
|
.row-status.success { background: #D7F7E0; color: #0E6245; }
|
|
|
|
.input-area {
|
|
padding: 18px 24px;
|
|
border-top: 1px solid var(--border);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.input-field {
|
|
display: flex;
|
|
gap: 12px;
|
|
align-items: center;
|
|
background: var(--input-bg);
|
|
border: 2px solid var(--border);
|
|
border-radius: 12px;
|
|
padding: 12px 16px;
|
|
transition: border-color 0.2s;
|
|
}
|
|
|
|
.input-field.active { border-color: #4a4a7a; }
|
|
|
|
.input-text {
|
|
flex: 1;
|
|
font-size: 15px;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.input-text.typing { color: var(--text); }
|
|
|
|
.send-btn {
|
|
width: 40px;
|
|
height: 40px;
|
|
background: linear-gradient(135deg, var(--purple), var(--purple-end));
|
|
border-radius: 8px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.send-btn svg { width: 20px; height: 20px; }
|
|
|
|
.replay-btn {
|
|
position: fixed;
|
|
bottom: 20px;
|
|
right: 20px;
|
|
padding: 10px 20px;
|
|
background: var(--purple);
|
|
color: white;
|
|
border: none;
|
|
border-radius: 8px;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
opacity: 0;
|
|
transition: opacity 0.3s;
|
|
z-index: 100;
|
|
}
|
|
|
|
.replay-btn.visible { opacity: 1; }
|
|
.replay-btn:hover { background: var(--purple-end); }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="simulation-container">
|
|
<div class="canvas" id="canvas">
|
|
<div class="chat-window">
|
|
<div class="titlebar">
|
|
<div class="traffic-lights">
|
|
<div class="light red"></div>
|
|
<div class="light yellow"></div>
|
|
<div class="light green"></div>
|
|
</div>
|
|
<div class="title">AI Assistant</div>
|
|
<div style="width: 48px;"></div>
|
|
</div>
|
|
|
|
<div class="messages" id="messages">
|
|
<!-- User message -->
|
|
<div class="message user" id="userMessage"></div>
|
|
|
|
<!-- AI typing -->
|
|
<div class="typing-indicator" id="aiTyping">
|
|
<div class="typing-dot"></div>
|
|
<div class="typing-dot"></div>
|
|
<div class="typing-dot"></div>
|
|
</div>
|
|
|
|
<!-- AI response with embed -->
|
|
<div class="ai-response-container" id="aiContainer" style="display: none;">
|
|
<div class="message ai" id="aiMessage"></div>
|
|
|
|
<div class="embed-wrapper" id="embedWrapper">
|
|
<div class="stripe-embed">
|
|
<div class="embed-header">
|
|
<div class="stripe-logo">stripe</div>
|
|
<div>
|
|
<div class="embed-title">Payment Recovery</div>
|
|
<div class="embed-subtitle">3 opportunities found</div>
|
|
</div>
|
|
</div>
|
|
<div class="embed-stats">
|
|
<div>
|
|
<div class="stat-value">$1,247</div>
|
|
<div class="stat-label">Recoverable</div>
|
|
</div>
|
|
<div>
|
|
<div class="stat-value" style="color: #1A1F36;">87%</div>
|
|
<div class="stat-label">Success Rate</div>
|
|
</div>
|
|
</div>
|
|
<div class="embed-row" id="row1">
|
|
<div class="row-email">john@example.com</div>
|
|
<div class="row-amount">$449</div>
|
|
<div class="row-status pending">Ready to retry</div>
|
|
</div>
|
|
<div class="embed-row" id="row2">
|
|
<div class="row-email">sarah@company.co</div>
|
|
<div class="row-amount">$299</div>
|
|
<div class="row-status success">Card updated</div>
|
|
</div>
|
|
<div class="embed-row" id="row3">
|
|
<div class="row-email">mike@startup.io</div>
|
|
<div class="row-amount">$499</div>
|
|
<div class="row-status pending">Ready to retry</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Follow-up insight message -->
|
|
<div class="message ai insight" id="insightMessage" style="display: none;"></div>
|
|
</div>
|
|
|
|
<div class="input-area">
|
|
<div class="input-field" id="inputField">
|
|
<div class="input-text" id="inputText">Type a message...</div>
|
|
<div class="send-btn">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
|
|
<path d="M22 2L11 13M22 2L15 22L11 13L2 9L22 2Z"/>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button class="replay-btn" id="replayBtn" onclick="startAnimation()">↻ Replay</button>
|
|
|
|
<script>
|
|
const USER_QUESTION = "What patterns do you see in my transaction data?";
|
|
const AI_PROCESSING = "Analyzing your payment data...";
|
|
const AI_INSIGHT = "Found $1,247 in recoverable revenue. 2 customers have updated payment methods — I can retry those charges now with a 87% success rate.";
|
|
|
|
const canvas = document.getElementById('canvas');
|
|
const inputField = document.getElementById('inputField');
|
|
const inputText = document.getElementById('inputText');
|
|
const userMessage = document.getElementById('userMessage');
|
|
const aiTyping = document.getElementById('aiTyping');
|
|
const aiContainer = document.getElementById('aiContainer');
|
|
const aiMessage = document.getElementById('aiMessage');
|
|
const embedWrapper = document.getElementById('embedWrapper');
|
|
const insightMessage = document.getElementById('insightMessage');
|
|
const replayBtn = document.getElementById('replayBtn');
|
|
|
|
// Smooth camera movement - fewer positions, more deliberate
|
|
function setCamera(zoom, x, y, duration = 1) {
|
|
canvas.style.transition = `transform ${duration}s cubic-bezier(0.25, 0.1, 0.25, 1)`;
|
|
canvas.style.transform = `scale(${zoom}) translate(${-x}px, ${-y}px)`;
|
|
}
|
|
|
|
function typeText(element, text, callback, charDelay = 35) {
|
|
let i = 0;
|
|
element.innerHTML = '';
|
|
|
|
function type() {
|
|
if (i < text.length) {
|
|
element.innerHTML = text.slice(0, i + 1) + '<span class="cursor"></span>';
|
|
i++;
|
|
setTimeout(type, charDelay);
|
|
} else {
|
|
element.innerHTML = text;
|
|
if (callback) setTimeout(callback, 200);
|
|
}
|
|
}
|
|
type();
|
|
}
|
|
|
|
function startAnimation() {
|
|
// Reset everything
|
|
replayBtn.classList.remove('visible');
|
|
userMessage.classList.remove('visible');
|
|
userMessage.innerHTML = '';
|
|
aiTyping.classList.remove('visible');
|
|
aiContainer.style.display = 'none';
|
|
aiMessage.classList.remove('visible');
|
|
aiMessage.innerHTML = '';
|
|
embedWrapper.classList.remove('visible');
|
|
insightMessage.style.display = 'none';
|
|
insightMessage.classList.remove('visible');
|
|
insightMessage.innerHTML = '';
|
|
['row1', 'row2', 'row3'].forEach(id => document.getElementById(id).classList.remove('visible'));
|
|
inputField.classList.remove('active');
|
|
inputText.classList.remove('typing');
|
|
inputText.innerHTML = 'Type a message...';
|
|
|
|
// PHASE 1: Zoom into input area
|
|
setCamera(1.5, 180, 520, 0);
|
|
|
|
setTimeout(() => {
|
|
// User starts typing
|
|
inputField.classList.add('active');
|
|
inputText.classList.add('typing');
|
|
|
|
let charIndex = 0;
|
|
const typeInterval = setInterval(() => {
|
|
if (charIndex <= USER_QUESTION.length) {
|
|
inputText.innerHTML = USER_QUESTION.slice(0, charIndex) + '<span class="cursor"></span>';
|
|
charIndex++;
|
|
} else {
|
|
clearInterval(typeInterval);
|
|
|
|
// PHASE 2: Message sent - smooth zoom out
|
|
setTimeout(() => {
|
|
inputText.innerHTML = 'Type a message...';
|
|
inputText.classList.remove('typing');
|
|
inputField.classList.remove('active');
|
|
|
|
userMessage.innerHTML = USER_QUESTION;
|
|
userMessage.classList.add('visible');
|
|
|
|
// Zoom out smoothly to show conversation
|
|
setCamera(1.0, 150, 150, 1.2);
|
|
|
|
// PHASE 3: AI thinking
|
|
setTimeout(() => {
|
|
aiTyping.classList.add('visible');
|
|
|
|
// PHASE 4: AI responds
|
|
setTimeout(() => {
|
|
aiTyping.classList.remove('visible');
|
|
aiContainer.style.display = 'flex';
|
|
aiMessage.innerHTML = '<span class="spinner"></span>' + AI_PROCESSING;
|
|
aiMessage.classList.add('visible');
|
|
|
|
// PHASE 5: Show embed
|
|
setTimeout(() => {
|
|
embedWrapper.classList.add('visible');
|
|
|
|
// Stagger rows
|
|
setTimeout(() => document.getElementById('row1').classList.add('visible'), 200);
|
|
setTimeout(() => document.getElementById('row2').classList.add('visible'), 350);
|
|
setTimeout(() => document.getElementById('row3').classList.add('visible'), 500);
|
|
|
|
// PHASE 6: Insight message
|
|
setTimeout(() => {
|
|
insightMessage.style.display = 'block';
|
|
|
|
// Small camera adjustment to show insight
|
|
setCamera(0.95, 150, 200, 0.8);
|
|
|
|
setTimeout(() => {
|
|
typeText(insightMessage, AI_INSIGHT, () => {
|
|
insightMessage.classList.add('visible');
|
|
|
|
// Show replay
|
|
setTimeout(() => {
|
|
replayBtn.classList.add('visible');
|
|
}, 800);
|
|
}, 25);
|
|
}, 100);
|
|
}, 1000);
|
|
|
|
}, 600);
|
|
}, 1000);
|
|
}, 800);
|
|
|
|
}, 400);
|
|
}
|
|
}, 35);
|
|
|
|
}, 600);
|
|
}
|
|
|
|
// Auto-start
|
|
setTimeout(startAnimation, 400);
|
|
</script>
|
|
</body>
|
|
</html>
|