watercooler
Version:
A beautiful 3D visualization of your mailbox messages as a village of coworkers
912 lines (789 loc) • 26.5 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>Watercooler</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
overflow: hidden;
}
#canvas-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
/* Collapsible Send Panel - Top Left */
.send-panel {
position: fixed;
top: 20px;
left: 20px;
width: 320px;
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(20px);
border-radius: 16px;
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
z-index: 100;
overflow: hidden;
transition: all 0.3s ease;
}
.send-panel.collapsed {
width: auto;
min-width: 180px;
}
.send-panel.collapsed .send-form {
display: none;
}
.send-header {
padding: 12px 16px;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.send-header:hover {
background: rgba(255, 255, 255, 0.05);
}
.send-header h2 {
font-size: 0.9rem;
color: white;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
.collapse-btn {
background: none;
border: none;
color: white;
font-size: 1.2rem;
cursor: pointer;
opacity: 0.7;
transition: opacity 0.2s;
}
.collapse-btn:hover {
opacity: 1;
}
.send-form {
padding: 16px;
}
.recipient-select {
width: 100%;
padding: 10px 12px;
margin-bottom: 10px;
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.2);
background: rgba(0, 0, 0, 0.2);
color: white;
font-size: 0.9rem;
cursor: pointer;
outline: none;
}
.recipient-select option {
background: #333;
color: white;
}
.message-input {
width: 100%;
padding: 10px 12px;
margin-bottom: 10px;
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.2);
background: rgba(0, 0, 0, 0.2);
color: white;
font-size: 0.9rem;
min-height: 60px;
resize: vertical;
outline: none;
font-family: inherit;
}
.message-input::placeholder {
color: rgba(255, 255, 255, 0.5);
}
.send-btn {
width: 100%;
padding: 10px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 10px;
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
}
.send-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.5);
}
/* Messages Panel - Slide from Right */
.messages-panel {
position: fixed;
top: 20px;
right: -400px;
width: 380px;
max-height: calc(100vh - 40px);
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(20px);
border-radius: 16px;
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
z-index: 100;
overflow: hidden;
display: flex;
flex-direction: column;
transition: right 0.3s ease;
}
.messages-panel.open {
right: 20px;
}
.messages-header {
padding: 16px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
justify-content: space-between;
align-items: center;
}
.messages-header h2 {
font-size: 1rem;
color: white;
font-weight: 600;
}
.close-btn {
background: none;
border: none;
color: white;
font-size: 1.5rem;
cursor: pointer;
opacity: 0.7;
transition: opacity 0.2s;
}
.close-btn:hover {
opacity: 1;
}
.mark-all-read-btn {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
color: rgba(255, 255, 255, 0.8);
font-size: 0.75rem;
padding: 4px 10px;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
white-space: nowrap;
}
.mark-all-read-btn:hover {
background: rgba(255, 255, 255, 0.2);
color: white;
}
.messages-container {
flex: 1;
overflow-y: auto;
padding: 16px;
}
.message-card {
background: rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 12px 12px 12px 20px;
margin-bottom: 10px;
transition: all 0.3s ease;
cursor: pointer;
}
.message-card:hover {
background: rgba(255, 255, 255, 0.15);
}
.message-card.unread {
border-left: 3px solid #ff6b6b;
background: rgba(255, 107, 107, 0.15);
}
.message-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 6px;
}
.message-sender {
font-weight: 600;
color: white;
font-size: 0.85rem;
}
.message-time {
font-size: 0.7rem;
color: rgba(255, 255, 255, 0.6);
}
.message-text {
color: rgba(255, 255, 255, 0.9);
font-size: 0.85rem;
line-height: 1.4;
white-space: pre-wrap;
word-wrap: break-word;
}
.message-text h1, .message-text h2, .message-text h3 {
margin: 8px 0;
color: white;
}
.message-text h1 { font-size: 1.2rem; }
.message-text h2 { font-size: 1.1rem; }
.message-text h3 { font-size: 1rem; }
.message-text strong {
color: white;
font-weight: 700;
}
.message-text code {
background: rgba(0, 0, 0, 0.3);
padding: 2px 6px;
border-radius: 4px;
font-family: 'Monaco', 'Menlo', monospace;
font-size: 0.8rem;
}
.message-text pre {
background: rgba(0, 0, 0, 0.3);
padding: 10px;
border-radius: 8px;
overflow-x: auto;
margin: 8px 0;
}
.message-text pre code {
background: transparent;
padding: 0;
}
.message-text hr {
border: none;
border-top: 1px solid rgba(255, 255, 255, 0.2);
margin: 12px 0;
}
.message-text blockquote {
border-left: 3px solid rgba(255, 255, 255, 0.4);
margin: 8px 0;
padding-left: 12px;
color: rgba(255, 255, 255, 0.7);
}
.message-text ul, .message-text ol {
margin: 8px 0;
padding-left: 24px;
}
.message-text li {
margin: 2px 0;
}
/* Toggle Messages Button */
.toggle-messages-btn {
position: fixed;
top: 20px;
right: 20px;
padding: 12px 20px;
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 12px;
color: white;
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
z-index: 101;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}
.toggle-messages-btn:hover {
background: rgba(255, 255, 255, 0.25);
}
.toggle-messages-btn .badge {
background: #ff6b6b;
color: white;
padding: 2px 8px;
border-radius: 12px;
font-size: 0.75rem;
margin-left: 8px;
}
/* Toast notification */
.toast {
position: fixed;
bottom: 30px;
right: 30px;
background: rgba(76, 175, 80, 0.9);
backdrop-filter: blur(10px);
color: white;
padding: 14px 20px;
border-radius: 10px;
font-weight: 600;
opacity: 0;
transform: translateY(20px);
transition: all 0.3s ease;
z-index: 200;
}
.toast.show {
opacity: 1;
transform: translateY(0);
}
/* Empty state */
.empty-state {
text-align: center;
padding: 40px 20px;
color: rgba(255, 255, 255, 0.6);
}
/* Scrollbar */
.messages-container::-webkit-scrollbar {
width: 6px;
}
.messages-container::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
border-radius: 3px;
}
.messages-container::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 3px;
}
/* Mobile responsive styles */
@media (max-width: 768px) {
/* Send panel - full width at bottom */
.send-panel {
top: auto;
bottom: 0;
left: 0;
right: 0;
width: 100%;
max-width: 100%;
border-radius: 16px 16px 0 0;
border-bottom: none;
border-left: none;
border-right: none;
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.3);
}
.send-panel.collapsed {
width: 100%;
min-width: 100%;
transform: translateY(calc(100% - 75px));
}
.send-header {
padding: 14px 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.send-header h2 {
font-size: 1rem;
}
.collapse-btn {
font-size: 1.4rem;
padding: 8px 16px;
}
.send-form {
padding: 20px;
max-height: 50vh;
overflow-y: auto;
}
.recipient-select, .message-input, .send-btn {
font-size: 16px; /* Prevents zoom on iOS */
padding: 14px 16px;
margin-bottom: 12px;
}
.message-input {
min-height: 80px;
}
/* Messages panel - nearly full screen on mobile */
.messages-panel {
top: auto;
bottom: 0;
left: 0;
right: 0;
width: 100%;
max-width: 100%;
max-height: 90vh;
height: 90vh;
border-radius: 16px 16px 0 0;
transform: translateY(100%);
transition: transform 0.3s ease;
display: flex;
flex-direction: column;
}
.messages-panel.open {
right: 0;
transform: translateY(0);
}
.messages-header {
padding: 20px 24px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
flex-shrink: 0;
}
.messages-header h2 {
font-size: 1.2rem;
}
.close-btn {
font-size: 1.8rem;
padding: 8px;
}
.messages-container {
padding: 20px;
flex: 1;
max-height: calc(90vh - 80px);
}
.message-card {
padding: 16px 16px 16px 24px;
margin-bottom: 12px;
}
.message-sender {
font-size: 0.9rem;
}
.message-time {
font-size: 0.75rem;
}
.message-text {
font-size: 0.9rem;
}
/* Toggle messages button - repositioned */
.toggle-messages-btn {
top: 20px;
right: 20px;
padding: 14px 18px;
font-size: 1rem;
border-radius: 30px;
z-index: 101;
}
/* Toast notification - centered */
.toast {
left: 20px;
right: 20px;
bottom: auto;
top: 50%;
transform: translateY(-50%) translateY(20px);
text-align: center;
padding: 16px 24px;
font-size: 1rem;
}
.toast.show {
transform: translateY(-50%) translateY(0);
}
/* Desk dialog - full screen on mobile */
.house-dialog-content {
width: 100%;
max-width: 100%;
max-height: 95vh;
height: 95vh;
border-radius: 16px 16px 0 0;
margin: 0;
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
flex-direction: column;
}
.house-dialog-header {
padding: 20px 24px;
flex-shrink: 0;
}
.house-dialog-header h2 {
font-size: 1.2rem;
}
.house-dialog-tabs {
padding: 0 12px;
flex-shrink: 0;
}
.tab-btn {
padding: 16px 12px;
font-size: 0.9rem;
}
.house-dialog-body {
padding: 20px;
max-height: calc(95vh - 140px);
flex: 1;
overflow-y: auto;
}
.house-dialog-body .message-card {
padding: 16px 16px 16px 24px;
}
/* Empty state */
.empty-state {
padding: 60px 20px;
}
.empty-state p {
font-size: 1rem;
}
/* Choice buttons on mobile */
.message-choices {
gap: 6px;
margin-top: 10px;
padding-top: 10px;
}
.choice-btn {
padding: 10px 14px;
font-size: 0.9rem;
}
}
/* Extra small screens */
@media (max-width: 480px) {
.send-panel.collapsed {
transform: translateY(calc(100% - 70px));
}
.send-header {
padding: 12px 16px;
}
.send-header h2 {
font-size: 0.9rem;
}
.send-form {
padding: 16px;
}
.recipient-select, .message-input, .send-btn {
font-size: 16px;
padding: 12px 14px;
}
.toggle-messages-btn {
padding: 12px 16px;
font-size: 0.9rem;
}
.messages-panel {
max-height: 75vh;
}
.tab-btn {
font-size: 0.8rem;
padding: 12px 8px;
}
}
/* Desk Dialog */
.house-dialog {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
z-index: 300;
align-items: flex-end;
justify-content: center;
}
.house-dialog.active {
display: flex;
}
/* Desktop override for desk dialog */
@media (min-width: 769px) {
.house-dialog {
align-items: center;
}
.house-dialog-content {
position: relative;
bottom: auto;
left: auto;
right: auto;
border-radius: 20px;
}
}
/* Desktop desk dialog styles */
@media (min-width: 769px) {
.house-dialog-content {
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(20px);
border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
width: 500px;
max-width: 90%;
max-height: 80vh;
display: flex;
flex-direction: column;
}
}
/* Base desk dialog styles (shared) */
.house-dialog-content {
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
display: flex;
flex-direction: column;
}
.house-dialog-header {
padding: 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
justify-content: space-between;
align-items: center;
}
.house-dialog-header h2 {
color: white;
font-size: 1.2rem;
font-weight: 600;
}
.house-dialog-body {
padding: 20px;
overflow-y: auto;
max-height: 60vh;
}
.house-dialog-body .message-card {
background: rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 12px 12px 12px 20px;
margin-bottom: 10px;
}
.house-dialog-body .message-card.unread {
border-left: 3px solid #ff6b6b;
background: rgba(255, 107, 107, 0.15);
}
/* Tabs */
.house-dialog-tabs {
display: flex;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.tab-btn {
flex: 1;
padding: 12px;
background: transparent;
border: none;
color: rgba(255, 255, 255, 0.7);
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
}
.tab-btn:hover {
color: white;
background: rgba(255, 255, 255, 0.1);
}
.tab-btn.active {
color: white;
background: rgba(255, 255, 255, 0.15);
}
.tab-btn.active::after {
content: '';
position: absolute;
bottom: 0;
left: 20%;
right: 20%;
height: 3px;
background: #667eea;
border-radius: 2px;
}
.tab-badge {
background: #ff6b6b;
color: white;
padding: 2px 8px;
border-radius: 12px;
font-size: 0.75rem;
margin-left: 6px;
}
.tab-badge:empty,
.tab-badge[style*="display: none"] {
display: none;
}
/* Message choice buttons */
.message-choices {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.choice-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 20px;
padding: 8px 16px;
font-size: 0.85rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
}
.choice-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.5);
background: linear-gradient(135deg, #7b8ff0 0%, #8b5eb5 100%);
}
.choice-btn:active {
transform: translateY(0);
box-shadow: 0 2px 6px rgba(102, 126, 234, 0.3);
}
/* Darker variant for unread messages */
.message-card.unread .choice-btn {
background: linear-gradient(135deg, #4fd1c5 0%, #667eea 100%);
}
.message-card.unread .choice-btn:hover {
background: linear-gradient(135deg, #5ee4d8 0%, #7b8ff0 100%);
}
</style>
<!-- Markdown parser -->
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
</head>
<body>
<div id="canvas-container"></div>
<!-- Collapsible Send Panel -->
<div class="send-panel" id="send-panel">
<div class="send-header" onclick="toggleSendPanel()">
<h2>💬 Send Message</h2>
<button class="collapse-btn" id="collapse-btn">−</button>
</div>
<div class="send-form">
<select id="recipient-select" class="recipient-select">
<option value="">To: Select agent...</option>
</select>
<textarea id="message-input" class="message-input" placeholder="Type your message..."></textarea>
<button id="send-btn" class="send-btn">Send</button>
</div>
</div>
<!-- Toggle Messages Button -->
<button class="toggle-messages-btn" id="toggle-messages-btn" onclick="toggleMessagesPanel()">
📨 Messages <span class="badge" id="unread-badge" style="display: none;">0</span>
</button>
<!-- Messages Panel (Slide from Right) -->
<div class="messages-panel" id="messages-panel">
<div class="messages-header">
<h2>📨 Message History</h2>
<div style="display: flex; gap: 8px; align-items: center;">
<button class="mark-all-read-btn" onclick="markAllAsRead()">Mark all as read</button>
<button class="close-btn" onclick="toggleMessagesPanel()">×</button>
</div>
</div>
<div class="messages-container" id="messages-container">
<div class="empty-state">
<div style="font-size: 2rem; margin-bottom: 8px;">📭</div>
<p>No messages yet</p>
</div>
</div>
</div>
<!-- Desk Dialog - Shows messages with specific agent -->
<div class="house-dialog" id="house-dialog">
<div class="house-dialog-content">
<div class="house-dialog-header">
<h2 id="house-dialog-title">Messages</h2>
<button class="close-btn" onclick="closeDeskDialog()">×</button>
</div>
<div class="house-dialog-tabs">
<button class="tab-btn active" id="tab-received" onclick="switchTab('received')">
📥 Received <span id="received-count" class="tab-badge"></span>
</button>
<button class="tab-btn" id="tab-sent" onclick="switchTab('sent')">
📤 Sent <span id="sent-count" class="tab-badge"></span>
</button>
</div>
<div class="house-dialog-body" id="house-dialog-content">
<!-- Messages will be loaded here -->
</div>
</div>
</div>
<div class="toast" id="toast"><span id="toast-message">Message sent!</span></div>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.160.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
}
}
</script>
<script type="module" src="/app.js"></script>
</body>
</html>