anycoder-7c0fd5aa / index.html
defaultdolphin's picture
Upload folder using huggingface_hub
7c9548b verified
Raw
History Blame Contribute Delete
23.6 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Modern Message UI Simulator</title>
<!-- Importing FontAwesome for Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/*
* MODERN CSS RESET & VARIABLES
* Using CSS Custom Properties for easy theming
*/
:root {
--primary-color: #4f46e5;
--primary-hover: #4338ca;
--secondary-bg: #f3f4f6;
--white: #ffffff;
--text-dark: #111827;
--text-gray: #6b7280;
--text-light: #9ca3af;
--border-color: #e5e7eb;
--incoming-bubble: #e5e7eb;
--outgoing-bubble: #4f46e5;
--outgoing-text: #ffffff;
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
--header-height: 60px;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font-family);
background-color: #f9fafb;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
color: var(--text-dark);
}
/*
* HEADER SECTION
*/
header {
background-color: var(--white);
height: var(--header-height);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
border-bottom: 1px solid var(--border-color);
box-shadow: var(--shadow-sm);
z-index: 10;
}
.logo {
font-size: 1.25rem;
font-weight: 700;
color: var(--primary-color);
display: flex;
align-items: center;
gap: 10px;
}
.brand-link {
text-decoration: none;
color: var(--text-light);
font-size: 0.9rem;
transition: color 0.2s;
}
.brand-link:hover {
color: var(--primary-color);
}
/*
* MAIN LAYOUT
* Using CSS Grid for 2-column layout (Sidebar + Chat)
*/
main {
display: grid;
grid-template-columns: 320px 1fr;
flex: 1;
height: calc(100vh - var(--header-height));
overflow: hidden;
}
/*
* SIDEBAR (Contact List)
*/
.sidebar {
background-color: var(--white);
border-right: 1px solid var(--border-color);
display: flex;
flex-direction: column;
overflow-y: auto;
}
.search-bar {
padding: 15px;
border-bottom: 1px solid var(--border-color);
position: sticky;
top: 0;
background: var(--white);
z-index: 5;
}
.search-input-wrapper {
position: relative;
}
.search-input-wrapper i {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
color: var(--text-light);
font-size: 0.9rem;
}
.search-input {
width: 100%;
padding: 10px 10px 10px 35px;
border-radius: 8px;
border: 1px solid var(--border-color);
background-color: var(--secondary-bg);
font-size: 0.9rem;
outline: none;
transition: border-color 0.2s;
}
.search-input:focus {
border-color: var(--primary-color);
background-color: var(--white);
}
.contact-list {
list-style: none;
}
.contact-item {
display: flex;
align-items: center;
padding: 15px;
cursor: pointer;
transition: background-color 0.2s;
border-bottom: 1px solid transparent;
}
.contact-item:hover {
background-color: var(--secondary-bg);
}
.contact-item.active {
background-color: #eef2ff;
border-left: 4px solid var(--primary-color);
}
.avatar {
width: 48px;
height: 48px;
border-radius: 50%;
object-fit: cover;
margin-right: 15px;
border: 2px solid var(--white);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.contact-info {
flex: 1;
min-width: 0; /* Enables truncation */
}
.contact-name {
font-weight: 600;
font-size: 1rem;
margin-bottom: 4px;
color: var(--text-dark);
display: flex;
justify-content: space-between;
}
.last-message-time {
font-size: 0.75rem;
color: var(--text-light);
font-weight: 400;
}
.last-message {
font-size: 0.85rem;
color: var(--text-gray);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/*
* CHAT AREA
*/
.chat-area {
background-color: #f0f2f5;
display: flex;
flex-direction: column;
position: relative;
}
/* Chat Header */
.chat-header {
background-color: var(--white);
padding: 15px 20px;
display: flex;
align-items: center;
border-bottom: 1px solid var(--border-color);
box-shadow: var(--shadow-sm);
}
.chat-user-info {
display: flex;
align-items: center;
gap: 15px;
}
.user-status {
display: flex;
flex-direction: column;
}
.status-dot {
display: inline-block;
width: 8px;
height: 8px;
background-color: #10b981;
border-radius: 50%;
margin-right: 5px;
}
.status-text {
font-size: 0.8rem;
color: var(--text-gray);
}
.chat-actions {
margin-left: auto;
display: flex;
gap: 20px;
color: var(--primary-color);
font-size: 1.2rem;
cursor: pointer;
}
/* Messages Container */
.messages-container {
flex: 1;
padding: 20px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 15px;
background-image: radial-gradient(#d1d5db 1px, transparent 1px);
background-size: 20px 20px;
}
.message {
display: flex;
max-width: 70%;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.message.incoming {
align-self: flex-start;
}
.message.outgoing {
align-self: flex-end;
flex-direction: row-reverse;
}
.message-content {
padding: 12px 16px;
border-radius: 18px;
font-size: 0.95rem;
line-height: 1.4;
position: relative;
box-shadow: var(--shadow-sm);
}
.message.incoming .message-content {
background-color: var(--white);
color: var(--text-dark);
border-top-left-radius: 4px;
}
.message.outgoing .message-content {
background-color: var(--outgoing-bubble);
color: var(--outgoing-text);
border-top-right-radius: 4px;
}
.message-time {
font-size: 0.7rem;
margin-top: 5px;
opacity: 0.7;
text-align: right;
display: block;
}
.message.outgoing .message-time {
color: rgba(255, 255, 255, 0.8);
}
/* Input Area */
.input-area {
background-color: var(--white);
padding: 20px;
border-top: 1px solid var(--border-color);
display: flex;
align-items: center;
gap: 15px;
}
.attach-btn {
color: var(--text-gray);
font-size: 1.2rem;
cursor: pointer;
transition: color 0.2s;
}
.attach-btn:hover {
color: var(--primary-color);
}
.message-input {
flex: 1;
padding: 12px 20px;
border-radius: 25px;
border: 1px solid var(--border-color);
background-color: var(--secondary-bg);
font-size: 1rem;
outline: none;
transition: all 0.2s;
}
.message-input:focus {
background-color: var(--white);
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
}
.send-btn {
background-color: var(--primary-color);
color: var(--white);
width: 45px;
height: 45px;
border-radius: 50%;
border: none;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: background-color 0.2s, transform 0.1s;
}
.send-btn:hover {
background-color: var(--primary-hover);
}
.send-btn:active {
transform: scale(0.95);
}
/*
* RESPONSIVE DESIGN
* Mobile Layout Adjustments
*/
@media (max-width: 768px) {
main {
grid-template-columns: 1fr;
}
.sidebar {
position: absolute;
width: 100%;
height: 100%;
z-index: 20;
transform: translateX(0);
transition: transform 0.3s ease-in-out;
}
.sidebar.hidden {
transform: translateX(-100%);
}
.back-btn {
display: block !important;
margin-right: 10px;
cursor: pointer;
color: var(--text-dark);
}
}
.back-btn {
display: none;
font-size: 1.2rem;
}
/* Empty State */
.empty-state {
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
color: var(--text-gray);
text-align: center;
}
.empty-state i {
font-size: 4rem;
margin-bottom: 20px;
color: var(--text-light);
}
.no-chat-selected .empty-state {
display: flex;
}
.no-chat-selected .chat-header,
.no-chat-selected .messages-container,
.no-chat-selected .input-area {
display: none;
}
</style>
</head>
<body>
<header>
<div class="logo">
<i class="fa-solid fa-comments"></i>
<span>MsgReader</span>
</div>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="brand-link">
Built with anycoder
</a>
</header>
<main>
<!-- Sidebar: Contact List -->
<aside class="sidebar" id="sidebar">
<div class="search-bar">
<div class="search-input-wrapper">
<i class="fa-solid fa-search"></i>
<input type="text" class="search-input" placeholder="Search conversations..." id="searchInput">
</div>
</div>
<ul class="contact-list" id="contactList">
<!-- Contacts will be injected here via JS -->
</ul>
</aside>
<!-- Main Chat Area -->
<section class="chat-area no-chat-selected" id="chatArea">
<!-- Empty State (Shown when no chat is selected) -->
<div class="empty-state">
<i class="fa-regular fa-paper-plane"></i>
<h2>Select a conversation</h2>
<p>Choose a contact from the list to view messages.</p>
</div>
<!-- Active Chat Interface -->
<div class="chat-header">
<div class="chat-user-info">
<i class="fa-solid fa-arrow-left back-btn" id="backBtn"></i>
<img src="" alt="User" class="avatar" id="currentAvatar">
<div class="user-status">
<span class="contact-name" id="currentName">User Name</span>
<span class="status-text"><span class="status-dot"></span>Online</span>
</div>
</div>
<div class="chat-actions">
<i class="fa-solid fa-phone"></i>
<i class="fa-solid fa-video"></i>
<i class="fa-solid fa-circle-info"></i>
</div>
</div>
<div class="messages-container" id="messagesContainer">
<!-- Messages will be injected here via JS -->
</div>
<div class="input-area">
<i class="fa-solid fa-paperclip attach-btn"></i>
<i class="fa-regular fa-image attach-btn"></i>
<input type="text" class="message-input" placeholder="Type a message..." id="messageInput">
<button class="send-btn" id="sendBtn">
<i class="fa-solid fa-paper-plane"></i>
</button>
</div>
</section>
</main>
<script>
// Mock Data: Contacts and their message history
const contactsData = [
{
id: 1,
name: "Alice Johnson",
avatar: "https://picsum.photos/seed/alice/100/100",
time: "10:30 AM",
messages: [
{ id: 1, text: "Hey! Are we still on for lunch?", type: "incoming", time: "10:28 AM" },
{ id: 2, text: "Yes, definitely. 12:30 at the usual spot?", type: "outgoing", time: "10:29 AM" },
{ id: 3, text: "Perfect. See you there!", type: "incoming", time: "10:30 AM" }
]
},
{
id: 2,
name: "Bob Smith",
avatar: "https://picsum.photos/seed/bob/100/100",
time: "Yesterday",
messages: [
{ id: 1, text: "Did you see the game last night?", type: "incoming", time: "Yesterday" },
{ id: 2, text: "No, I missed it. Who won?", type: "outgoing", time: "Yesterday" },
{ id: 3, text: "It was incredible! We won by 2 points.", type: "incoming", time: "Yesterday" }
]
},
{
id: 3,
name: "Charlie Davis",
avatar: "https://picsum.photos/seed/charlie/100/100",
time: "Monday",
messages: [
{ id: 1, text: "Can you send me the project files?", type: "incoming", time: "Monday" },
{ id: 2, text: "Sure thing, uploading them now.", type: "outgoing", time: "Monday" }
]
},
{
id: 4,
name: "Diana Evans",
avatar: "https://picsum.photos/seed/diana/100/100",
time: "Sunday",
messages: [
{ id: 1, text: "Happy Birthday! 🎂", type: "incoming", time: "Sunday" },
{ id: 2, text: "Thank you so much! 🥳", type: "outgoing", time: "Sunday" }
]
},
{
id: 5,
name: "Ethan Hunt",
avatar: "https://picsum.photos/seed/ethan/100/100",
time: "Jan 20",
messages: [
{ id: 1, text: "Mission accomplished.", type: "incoming", time: "Jan 20" },
]
}
];
let activeContactId = null;
// DOM Elements
const contactListEl = document.getElementById('contactList');
const messagesContainerEl = document.getElementById('messagesContainer');
const messageInputEl = document.getElementById('messageInput');
const sendBtnEl = document.getElementById('sendBtn');
const currentAvatarEl = document.getElementById('currentAvatar');
const currentNameEl = document.getElementById('currentName');
const chatAreaEl = document.getElementById('chatArea');
const sidebarEl = document.getElementById('sidebar');
const backBtnEl = document.getElementById('backBtn');
const searchInputEl = document.getElementById('searchInput');
// Initialize App
function init() {
renderContacts(contactsData);
setupEventListeners();
}
// Render Contact List
function renderContacts(contacts) {
contactListEl.innerHTML = '';
contacts.forEach(contact => {
const lastMsg = contact.messages[contact.messages.length - 1];
const li = document.createElement('li');
li.className = `contact-item ${activeContactId === contact.id ? 'active' : ''}`;
li.onclick = () => selectContact(contact.id);
li.innerHTML = `
<img src="${contact.avatar}" alt="${contact.name}" class="avatar">
<div class="contact-info">
<div class="contact-name">
${contact.name}
<span class="last-message-time">${contact.time}</span>
</div>
<div class="last-message">${lastMsg ? lastMsg.text : 'No messages'}</div>
</div>
`;
contactListEl.appendChild(li);
});
}
// Select a Contact
function selectContact(id) {
activeContactId = id;
const contact = contactsData.find(c => c.id === id);
// Update UI State
chatAreaEl.classList.remove('no-chat-selected');
// Mobile Responsive: Hide sidebar
if (window.innerWidth <= 768) {
sidebarEl.classList.add('hidden');
}
// Update Header Info
currentNameEl.textContent = contact.name;
currentAvatarEl.src = contact.avatar;
// Highlight active contact in sidebar
renderContacts(contactsData.filter(c => c.name.toLowerCase().includes(searchInputEl.value.toLowerCase())));
// Render Messages
renderMessages(contact.messages);
// Scroll to bottom
scrollToBottom();
// Focus input
messageInputEl.focus();
}
// Render Messages
function renderMessages(messages) {
messagesContainerEl.innerHTML = '';
messages.forEach(msg => {
const div = document.createElement('div');
div.className = `message ${msg.type}`;
div.innerHTML = `
<div class="message-content">
${msg.text}
<span class="message-time">${msg.time}</span>
</div>
`;
messagesContainerEl.appendChild(div);
});
}
// Send Message
function sendMessage() {
const text = messageInputEl.value.trim();
if (!text || !activeContactId) return;
const contact = contactsData.find(c => c.id === activeContactId);
const now = new Date();
const timeString = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
// Add outgoing message to data
const newMessage = {
id: contact.messages.length + 1,
text: text,
type: 'outgoing',
time: timeString
};
contact.messages.push(newMessage);
// Update UI
renderMessages(contact.messages);
messageInputEl.value = '';
scrollToBottom();
// Update contact list preview (sort by last message logic could go here)
renderContacts(contactsData);
// Simulate Reply after 1.5 seconds
setTimeout(() => {
simulateReply(contact);
}, 1500);
}
// Simulate Incoming Reply
function simulateReply(contact) {
// Only reply if still looking at this chat
if (activeContactId !== contact.id) return;
const responses = ["That sounds great!", "I'll check and get back to you.", "Interesting...", "Ok.", "Can you elaborate?"];
const randomResponse = responses[Math.floor(Math.random() * responses.length)];
const now = new Date();
const timeString = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
const replyMsg = {
id: contact.messages.length + 1,
text: randomResponse,
type: 'incoming',
time: timeString
};
contact.messages.push(replyMsg);
renderMessages(contact.messages);
scrollToBottom();
renderContacts(contactsData);
}
function scrollToBottom() {
messagesContainerEl.scrollTop = messagesContainerEl.scrollHeight;
}
// Event Listeners
function setupEventListeners() {
sendBtnEl.addEventListener('click', sendMessage);
messageInputEl.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
sendMessage();
}
});
// Search Filter
searchInputEl.addEventListener('input', (e) => {
const term = e.target.value.toLowerCase();
const filtered = contactsData.filter(c => c.name.toLowerCase().includes(term));
renderContacts(filtered);
});
// Mobile Back Button
backBtnEl.addEventListener('click', () => {
sidebarEl.classList.remove('hidden');
activeContactId = null;
chatAreaEl.classList.add('no-chat-selected');
// Remove active class from list
document.querySelectorAll('.contact-item').forEach(el => el.classList.remove('active'));
});
}
// Start
init();
</script>
</body>
</html>