<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Agentforce - Local Attractions Guide</title>
<link rel="stylesheet" href="agentforce.css">
</head>
<body>
<div class="agentforce-container">
<div class="agentforce-header">
<div class="agentforce-title">Agentforce</div>
</div>
<div class="agentforce-chat-area" id="agentforce-chat-area">
<div class="agentforce-width-maintainer" style="width: 100%; height: 0; overflow: hidden;"></div>
<!-- Message 1: Agent Greeting -->
<div class="agentforce-message agentforce-message-1" id="message-1">
<div class="agentforce-avatar agentforce-agent-avatar"></div>
<div class="agentforce-message-content">
<div class="agentforce-agent-bubble">
Good afternoon! I'm LocalGuide, your digital concierge. How can I help enhance your stay with us today?
</div>
</div>
</div>
<!-- Message 2: User Response -->
<div class="agentforce-message agentforce-user-message agentforce-message-2" id="message-2">
<div class="agentforce-avatar agentforce-user-avatar">
<svg class="agentforce-user-icon" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 8C10.21 8 12 6.21 12 4C12 1.79 10.21 0 8 0C5.79 0 4 1.79 4 4C4 6.21 5.79 8 8 8ZM8 10C5.33 10 0 11.34 0 14V16H16V14C16 11.34 10.67 10 8 10Z" /></svg>
</div>
<div class="agentforce-message-content">
<div class="agentforce-user-bubble">
Hi! I have this afternoon free and was hoping to explore some local attractions. Do you have any suggestions?
</div>
</div>
</div>
<!-- Message 3: Agent Response -->
<div class="agentforce-message agentforce-message-3" id="message-3">
<div class="agentforce-avatar agentforce-agent-avatar"></div>
<div class="agentforce-message-content">
<div class="agentforce-agent-bubble">
I'd be happy to help you make the most of your afternoon! To offer personalized recommendations, could you share what kinds of activities interest you? Are you looking for outdoor adventures, cultural experiences, food and drink, or shopping?
</div>
</div>
</div>
<!-- Message 4: User Response -->
<div class="agentforce-message agentforce-user-message agentforce-message-4" id="message-4">
<div class="agentforce-avatar agentforce-user-avatar">
<svg class="agentforce-user-icon" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 8C10.21 8 12 6.21 12 4C12 1.79 10.21 0 8 0C5.79 0 4 1.79 4 4C4 6.21 5.79 8 8 8ZM8 10C5.33 10 0 11.34 0 14V16H16V14C16 11.34 10.67 10 8 10Z" /></svg>
</div>
<div class="agentforce-message-content">
<div class="agentforce-user-bubble">
I'm mainly interested in cultural experiences and trying local cuisine. I enjoy art and history, and I'd love to get a taste of what makes this area special.
</div>
</div>
</div>
<!-- Message 5: Record Card -->
<div class="agentforce-message agentforce-message-5" id="message-5">
<div class="agentforce-avatar agentforce-agent-avatar"></div>
<div class="agentforce-message-content">
<div class="agentforce-record-card">
<div class="agentforce-record-header">
<div class="agentforce-record-icon"></div>
<div class="agentforce-record-title">Local Recommendations</div>
</div>
<div class="agentforce-record-body">
<div class="agentforce-record-field">
<div class="agentforce-record-label">SEASONAL HIGHLIGHTS</div>
<div class="agentforce-record-value agentforce-record-value-highlight">Spring Botanical Garden Festival (2 miles)</div>
</div>
<div class="agentforce-record-field">
<div class="agentforce-record-label">TODAY'S WEATHER</div>
<div class="agentforce-record-value agentforce-record-value-normal">72°F, Partly Cloudy, Perfect for Outdoor Activities</div>
</div>
<div class="agentforce-record-field">
<div class="agentforce-record-label">LOCAL EVENTS</div>
<div class="agentforce-record-value agentforce-record-value-normal">Farmers Market (1-5pm), Art Walk (2-8pm)</div>
</div>
<div class="agentforce-record-field">
<div class="agentforce-record-label">GUEST DISCOUNT CODE</div>
<div class="agentforce-record-value agentforce-record-value-highlight">SPRING25 - 25% off at partner attractions</div>
</div>
<div class="agentforce-button-row">
<button class="agentforce-action-button">View All Activities</button>
<button class="agentforce-action-button">Transportation Options</button>
</div>
</div>
</div>
</div>
</div>
<div class="agentforce-message agentforce-user-message agentforce-message-5" id="message-5">
<div class="agentforce-avatar agentforce-user-avatar">
<svg class="agentforce-user-icon" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 8C10.21 8 12 6.21 12 4C12 1.79 10.21 0 8 0C5.79 0 4 1.79 4 4C4 6.21 5.79 8 8 8ZM8 10C5.33 10 0 11.34 0 14V16H16V14C16 11.34 10.67 10 8 10Z" /></svg>
</div>
<div class="agentforce-message-content">
<div class="agentforce-user-bubble">
I love cultural experiences and local food! The Art Walk sounds interesting. Is it within walking distance? And are there any good local restaurants along the way?
</div>
</div>
</div>
<!-- Message 7: Agent Response -->
<div class="agentforce-message agentforce-message-7" id="message-7">
<div class="agentforce-avatar agentforce-agent-avatar"></div>
<div class="agentforce-message-content">
<div class="agentforce-agent-bubble">
The Art Walk is absolutely perfect for you! It's just a 10-minute walk from our hotel and features 8 galleries in the historic district. I recommend starting at 2pm and stopping at Riverside Bistro around 4pm - they have a fantastic early dinner menu featuring local cuisine. Your SPRING25 code gets you a complimentary appetizer there!
</div>
</div>
</div>
<!-- Message 8: User Response -->
<div class="agentforce-message agentforce-user-message agentforce-message-8" id="message-8">
<div class="agentforce-avatar agentforce-user-avatar">
<svg class="agentforce-user-icon" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 8C10.21 8 12 6.21 12 4C12 1.79 10.21 0 8 0C5.79 0 4 1.79 4 4C4 6.21 5.79 8 8 8ZM8 10C5.33 10 0 11.34 0 14V16H16V14C16 11.34 10.67 10 8 10Z" /></svg>
</div>
<div class="agentforce-message-content">
<div class="agentforce-user-bubble">
That sounds perfect! Is there a map of the Art Walk route I can access?
</div>
</div>
</div>
<!-- Message 9: Agent Response -->
<div class="agentforce-message agentforce-message-9" id="message-9">
<div class="agentforce-avatar agentforce-agent-avatar"></div>
<div class="agentforce-message-content">
<div class="agentforce-agent-bubble">
I've sent a digital map of the Art Walk route to your hotel app, and you can also pick up a printed guide at the front desk.
</div>
</div>
</div>
<!-- Message 10: User Response -->
<div class="agentforce-message agentforce-user-message agentforce-message-10" id="message-10">
<div class="agentforce-avatar agentforce-user-avatar">
<svg class="agentforce-user-icon" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 8C10.21 8 12 6.21 12 4C12 1.79 10.21 0 8 0C5.79 0 4 1.79 4 4C4 6.21 5.79 8 8 8ZM8 10C5.33 10 0 11.34 0 14V16H16V14C16 11.34 10.67 10 8 10Z" /></svg>
</div>
<div class="agentforce-message-content">
<div class="agentforce-user-bubble">
Thank you so much! This sounds like exactly what I was hoping for today. I'll pick up the map on my way out.
</div>
</div>
</div>
</div>
<div class="agentforce-input-area">
<div class="agentforce-input-box">Ask about local attractions or activities...</div>
</div>
</div>
<script src="agentforce.js"></script>
</body>
</html>
/* Essential styles only */
.agentforce-container {
width: 100%;
max-width: 400px;
height: 600px;
margin: 0 auto;
background: white;
border: 1px solid #ccc;
border-radius: 8px;
overflow: hidden;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro", "Segoe UI", Roboto, sans-serif;
display: flex;
flex-direction: column;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.agentforce-header {
padding: 12px 16px;
border-bottom: 1px solid #eee;
background: white;
z-index: 10;
}
.agentforce-title {
font-size: 18px;
font-weight: 500;
color: #03234D;
}
.agentforce-chat-area {
flex: 1;
overflow-y: auto;
padding: 16px;
background: white;
scroll-behavior: smooth;
/* Force minimum content width */
min-width: 368px; /* 400px container - 16px padding each side */
}
.agentforce-message {
margin-bottom: 16px;
display: flex;
align-items: flex-start;
gap: 12px;
opacity: 0;
transform: translateY(10px);
animation-name: fadeIn;
animation-duration: 0.5s;
animation-timing-function: ease-out;
animation-fill-mode: forwards;
animation-delay: var(--delay);
width: 100%; /* Ensure message takes full width */
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.agentforce-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
flex-shrink: 0;
}
.agentforce-agent-avatar {
background: #D6E6FF;
background-image: url("https://cdn.prod.website-files.com/66aa487c3d059183dbec419e/6813d788e5c463cc7821be59_Agentforce-RGB-stroke-icon.svg");
background-size: 20px 20px;
background-position: center;
background-repeat: no-repeat;
}
.agentforce-user-avatar {
background: #0B827C;
display: flex;
align-items: center;
justify-content: center;
}
.agentforce-user-icon {
width: 16px;
height: 16px;
fill: white;
}
.agentforce-message-content {
flex: 1;
min-width: 0;
width: calc(100% - 44px); /* Full width minus avatar and gap */
}
.agentforce-agent-bubble {
background: #f3f4f6;
border-radius: 12px;
border-top-left-radius: 2px;
padding: 10px 12px;
color: #2E2E2E;
font-size: 13px;
line-height: 1.4;
width: 100%;
box-sizing: border-box;
}
.agentforce-user-bubble {
background: #0250D9;
border-radius: 12px;
border-bottom-right-radius: 2px;
padding: 10px 12px;
color: white;
font-size: 13px;
line-height: 1.4;
width: 100%;
box-sizing: border-box;
}
.agentforce-record-card {
border: 1px solid #eee;
border-radius: 12px;
overflow: hidden;
background: white;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
width: 100%;
box-sizing: border-box;
}
.agentforce-record-header {
padding: 12px 16px;
display: flex;
align-items: center;
gap: 8px;
border-bottom: 1px solid #eee;
width: 100%;
box-sizing: border-box;
}
.agentforce-record-icon {
width: 24px;
height: 24px;
background: #5867E8; /* Example color */
border-radius: 4px;
/* SVG Icon for Retail Store */
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M12 7c1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3 1.34 3 3 3zm0 8c-3.31 0-6 1.34-6 3v2h12v-2c0-1.66-2.69-3-6-3z'/%3E%3C/svg%3E"); /* Placeholder user icon, replace if needed */
background-size: 16px 16px;
background-position: center;
background-repeat: no-repeat;
}
.agentforce-record-title {
font-weight: 600;
font-size: 16px;
color: #03234D;
}
.agentforce-record-body {
padding: 12px 16px;
width: 100%;
box-sizing: border-box;
}
.agentforce-record-field {
margin-bottom: 8px;
width: 100%;
box-sizing: border-box;
}
.agentforce-record-label {
font-size: 12px;
font-weight: 700;
color: #5C5C5C;
margin-bottom: 2px;
width: 100%;
box-sizing: border-box;
}
.agentforce-record-value {
font-size: 12px;
line-height: 1.4;
width: 100%;
box-sizing: border-box;
}
.agentforce-record-value-normal {
color: #2E2E2E;
}
.agentforce-record-value-highlight {
color: #0250D9;
}
.agentforce-button-row {
display: flex;
margin-top: 12px; /* Adjusted margin for spacing */
width: 100%;
box-sizing: border-box;
gap: 8px; /* Add gap between buttons */
justify-content: center; /* Center buttons */
}
.agentforce-action-button {
height: 32px;
padding: 0 16px;
background: white;
border: 1px solid #5C5C5C;
border-radius: 9999px; /* Make buttons pill-shaped */
display: flex;
align-items: center;
justify-content: center;
color: #0250D9;
font-size: 14px;
cursor: pointer;
flex-grow: 1; /* Allow buttons to grow */
text-align: center;
}
.agentforce-action-button:hover {
background-color: #f0f0f0;
} /* Add hover effect */
/* Remove specific first/last child border adjustments */
.agentforce-action-button:first-child {
border-right: 1px solid #5C5C5C;
}
.agentforce-input-area {
padding: 8px 16px 16px;
border-top: 1px solid #eee;
background: white;
z-index: 10;
width: 100%;
box-sizing: border-box;
}
.agentforce-input-box {
height: 40px;
padding: 8px 12px;
background: white;
border-radius: 8px;
border: 1px solid #5C5C5C;
display: flex;
align-items: center;
color: #5C5C5C;
font-size: 13px;
width: 100%;
box-sizing: border-box;
}
.agentforce-user-message {
justify-content: flex-end;
}
.agentforce-user-message .agentforce-message-content {
order: -1;
}
/* Special hidden class */
.agentforce-message.hidden {
visibility: hidden;
opacity: 0;
transform: translateY(10px);
display: flex !important;
pointer-events: none;
height: 0;
margin: 0;
overflow: hidden;
}
// Auto-scrolling and message animation script
document.addEventListener('DOMContentLoaded', function() {
const chatArea = document.getElementById('agentforce-chat-area');
const messages = document.querySelectorAll('.agentforce-message');
const lastMessageIndex = messages.length - 1;
// Hide all messages initially
function hideAllMessages() {
messages.forEach(msg => {
msg.classList.add('hidden');
});
// No need to scroll to 0 initially, it will scroll as messages appear
}
// Calculate message display times based on content length
function calculateMessageTiming() {
// Base timing values (in milliseconds)
const baseDelayBetweenMessages = 500; // Delay *between* messages
const baseReadingTime = 1000; // Minimum time to read a message
const charReadingTime = 18; // Milliseconds per character
const recordCardReadingTime = 2000; // Extra time for record cards
// Start cumulative delay at 0 for the *first* message
let cumulativeDelay = 0;
const timings = [];
messages.forEach((message, index) => {
// Store the calculated timing for the *current* message
timings.push(cumulativeDelay);
// Calculate time to read *this* message to determine the delay for the *next* one
let currentContentLength = 0;
const bubbleElement = message.querySelector('.agentforce-agent-bubble, .agentforce-user-bubble');
if (bubbleElement) {
currentContentLength = bubbleElement.textContent.length;
}
const recordCard = message.querySelector('.agentforce-record-card');
let additionalTime = currentContentLength * charReadingTime;
if (recordCard) {
additionalTime += recordCardReadingTime;
}
// Add the reading time for the current message plus the base delay between messages
// to the cumulative delay for the *next* message.
cumulativeDelay += Math.max(baseReadingTime, additionalTime) + baseDelayBetweenMessages;
});
return timings;
}
// Display messages with calculated timing
function startSequence() {
hideAllMessages();
const timings = calculateMessageTiming();
messages.forEach((message, index) => {
// Use the calculated timing for when to *reveal* the message
setTimeout(() => {
// Make message visible
message.classList.remove('hidden');
// Scroll to the bottom after showing a message
// Use a small delay to ensure the element has height before scrolling
setTimeout(() => {
if (chatArea) {
chatArea.scrollTop = chatArea.scrollHeight;
}
}, 50); // Small delay to allow element to render and take up space
// Restart the sequence after showing the last message and adding a pause
if (index === lastMessageIndex) {
setTimeout(() => {
startSequence();
}, 6000); // 6-second pause before restart
}
}, timings[index]); // Use the calculated timing for THIS message
});
}
// Start the animation sequence almost immediately on DOM load
startSequence();
});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.