This commit is contained in:
2026-08-24 15:06:30 +01:00
parent eb85449d67
commit cdb04a39b9
5 changed files with 134 additions and 176 deletions
+18 -1
View File
@@ -99,7 +99,7 @@ nav {
position: fixed; top: 0; left: 0; position: fixed; top: 0; left: 0;
width: 100%; z-index: 20; width: 100%; z-index: 20;
display: grid; display: grid;
grid-template-columns: 1fr auto 1fr; grid-template-columns: auto 1fr auto; /* Left: Auto, Center: Grow, Right: Auto */
align-items: center; align-items: center;
gap: 1rem; gap: 1rem;
padding: 1.25rem 2.5rem; padding: 1.25rem 2.5rem;
@@ -148,6 +148,23 @@ nav {
} }
.nav-brand:hover { color: var(--accent); border-bottom: none; } .nav-brand:hover { color: var(--accent); border-bottom: none; }
.nav-timer {
grid-column: 3;
justify-self: end;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--danger-text);
border: 1px solid var(--danger);
border-radius: 2px;
padding: 0.3rem 0.7rem;
font-size: 0.72rem;
font-weight: 500;
letter-spacing: 0.08em;
background: var(--danger-soft);
white-space: nowrap;
}
.brand-sep { .brand-sep {
font-weight: 300; font-weight: 300;
color: var(--ink-dim); color: var(--ink-dim);
+34 -97
View File
@@ -7,109 +7,46 @@ html, body {
} }
body { body {
display: flex;
flex-direction: column;
overflow: hidden; overflow: hidden;
} }
/* ---- Mobile Menu Button (Hidden on Desktop) ---- */
.mobile-menu-btn {
display: none;
position: fixed;
bottom: 4rem;
left: 1.5rem;
z-index: 30;
background: var(--accent);
color: var(--bg);
border: none;
border-radius: 2px;
padding: 0.5rem 1rem;
font-size: 0.72rem;
font-weight: 500;
letter-spacing: 0.1em;
text-transform: uppercase;
cursor: pointer;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
/* ---- Layout ---- */ /* ---- Layout ---- */
.layout { .layout {
display: grid; display: grid;
grid-template-columns: 280px minmax(0, 1fr); grid-template-columns: 280px minmax(0, 1fr);
grid-template-rows: 52px minmax(0, 1fr); grid-template-rows: minmax(0, 1fr);
width: 100%; width: 100%;
height: calc(var(--app-height, 100dvh) - 40px); flex: 1;
min-height: 0;
overflow: hidden; overflow: hidden;
} }
/* ---- Topbar ---- */
.topbar {
grid-column: 1 / -1;
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0 1.5rem;
border-bottom: 1px solid var(--border);
background: var(--nav-bg);
backdrop-filter: blur(12px) saturate(120%);
-webkit-backdrop-filter: blur(12px) saturate(120%);
min-width: 0;
transition: background 0.3s ease;
}
.menu-button {
display: none;
background: transparent;
color: var(--ink);
border: 1px solid var(--border);
border-radius: 2px;
padding: 0.4rem 0.65rem;
font-size: 0.72rem;
font-weight: 500;
letter-spacing: 0.1em;
text-transform: uppercase;
cursor: pointer;
transition: border-color 0.2s, color 0.2s;
}
.menu-button:hover { border-color: var(--ink-muted); }
.topbar-brand {
display: inline-flex;
align-items: baseline;
gap: 0.35rem;
white-space: nowrap;
font-weight: 700;
font-size: 1rem;
letter-spacing: -0.02em;
color: var(--ink);
text-decoration: none;
}
.topbar-brand:hover { color: var(--accent); border-bottom: none; }
[data-theme="macchiato"] .topbar-brand {
color: #cad3f5;
text-shadow: 0 0 8px rgba(138, 173, 244, 0.25);
}
.topbar-brand .brand-sep {
font-weight: 300;
color: var(--ink-dim);
}
.topbar-brand .brand-sub {
font-weight: 400;
font-size: 0.72rem;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ink-muted);
}
.topbar-timer {
display: inline-flex;
align-items: center;
justify-content: center;
margin-left: auto;
font-size: 0.72rem;
font-weight: 500;
letter-spacing: 0.08em;
color: var(--danger-text);
background: var(--danger-soft);
padding: 0.3rem 0.7rem;
border-radius: 2px;
border: 1px solid var(--danger);
white-space: nowrap;
}
.drawer-backdrop {
display: none;
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.45);
z-index: 40;
backdrop-filter: blur(2px);
}
/* ---- Sidebar ---- */ /* ---- Sidebar ---- */
.sidebar { .sidebar {
grid-column: 1; grid-column: 1;
grid-row: 2; grid-row: 1;
border-right: 1px solid var(--border); border-right: 1px solid var(--border);
background: var(--bg-panel); background: var(--bg-panel);
backdrop-filter: blur(14px); backdrop-filter: blur(14px);
@@ -119,6 +56,7 @@ body {
overflow: hidden; overflow: hidden;
min-width: 0; min-width: 0;
transition: background 0.3s ease; transition: background 0.3s ease;
padding-top: 65px;
} }
.sidebar-header { .sidebar-header {
@@ -232,7 +170,7 @@ body {
/* ---- Main panel ---- */ /* ---- Main panel ---- */
.main-panel { .main-panel {
grid-column: 2; grid-column: 2;
grid-row: 2; grid-row: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@@ -266,6 +204,7 @@ body {
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
min-height: 50px; min-height: 50px;
flex-shrink: 0; flex-shrink: 0;
background: var(--bg); /* Match page bg to eliminate white strip */
} }
.active-copy { .active-copy {
@@ -451,11 +390,11 @@ body {
@media (max-width: 820px) { @media (max-width: 820px) {
.layout { .layout {
grid-template-columns: minmax(0, 1fr); grid-template-columns: minmax(0, 1fr);
grid-template-rows: 52px minmax(0, 1fr);
} }
.topbar { padding: 0 0.75rem; } .mobile-menu-btn {
.menu-button { display: inline-flex; } display: inline-flex;
}
.drawer-backdrop.visible { display: block; } .drawer-backdrop.visible { display: block; }
@@ -476,7 +415,6 @@ body {
.main-panel { .main-panel {
grid-column: 1; grid-column: 1;
grid-row: 2;
padding: 0.65rem; padding: 0.65rem;
align-items: stretch; align-items: stretch;
justify-content: stretch; justify-content: stretch;
@@ -495,10 +433,9 @@ body {
} }
@media (max-width: 460px) { @media (max-width: 460px) {
.topbar-brand .brand-sub { display: none; } .nav-brand .brand-sub { display: none; }
.topbar-brand .brand-sep { display: none; } .nav-brand .brand-sep { display: none; }
.main-panel { padding: 0.45rem; } .main-panel { padding: 0.45rem; }
.main-header { min-height: 46px; }
#supporter-messages { padding: 0.75rem; } #supporter-messages { padding: 0.75rem; }
.theme-toggle { width: 20px; height: 20px; bottom: 1rem; right: 0.75rem; } .theme-toggle { width: 20px; height: 20px; bottom: 1rem; right: 0.75rem; }
} }
+7 -7
View File
@@ -22,16 +22,16 @@
</head> </head>
<body> <body>
<nav> <nav>
<a href="https://fló.fo" class="back-link">← FLÓ.FO</a> <a href="https://fló.fo" class="back-link">← FLÓ.FO</a>
<a href="/" class="nav-brand"> <a href="/" class="nav-brand">
FLÓ <span class="brand-sep">/</span> <span class="brand-sub">USER TO SUPPORT CHAT // RUST</span> FLÓ <span class="brand-sep">/</span> <span class="brand-sub">USER TO SUPPORT CHAT // RUST</span>
</a> </a>
</nav> <span class="nav-timer" hx-get="/reset-countdown" hx-trigger="load, every 1s" hx-swap="innerHTML">Reset in --:--</span>
</nav>
<main class="page-body"> <main class="page-body">
<div class="card"> <div class="card">
<span class="timer" hx-get="/reset-countdown" hx-trigger="load, every 1s" hx-swap="innerHTML">Reset in --:--</span>
<h1>User to Support Chat</h1> <h1>User to Support Chat</h1>
<p>Rust-based real-time chat with separate user and supporter views. Conversations, messages, and rate limits reset every 20 minutes.</p> <p>Rust-based real-time chat with separate user and supporter views. Conversations, messages, and rate limits reset every 20 minutes.</p>
+68 -63
View File
@@ -22,76 +22,81 @@
</head> </head>
<body> <body>
<div class="layout"> <!-- STANDARD NAV (Same as user.html) -->
<header class="topbar"> <nav>
<button class="menu-button" type="button" onclick="openSidebar()">Chats</button> <a href="https://fló.fo" class="back-link">← FLÓ.FO</a>
<a href="/" class="topbar-brand"> <a href="/" class="nav-brand">
FLÓ <span class="brand-sep">/</span> <span class="brand-sub">USER TO SUPPORT CHAT // RUST</span> FLÓ <span class="brand-sep">/</span> <span class="brand-sub">USER TO SUPPORT CHAT // RUST</span>
</a> </a>
<span class="topbar-timer" hx-get="/reset-countdown" hx-trigger="load, every 1s" hx-swap="innerHTML">Reset in --:--</span> <span class="nav-timer" hx-get="/reset-countdown" hx-trigger="load, every 1s" hx-swap="innerHTML">Reset in --:--</span>
</header> </nav>
<div class="layout">
<aside id="sidebar" class="sidebar">
<div class="sidebar-header">
<span class="sidebar-title">Conversations</span>
<!-- Chats button moved here for desktop, or use mobile toggle -->
<button class="close-sidebar" type="button" onclick="closeSidebar()">Close</button>
</div>
<div id="session-list"
hx-get="/sessions"
hx-trigger="load, refresh, every 5s"
hx-swap="innerHTML">
<p class="no-sessions">Loading...</p>
</div>
</aside>
<section class="main-panel">
<div class="chat-shell">
<div class="main-header">
<div class="active-copy">
<span id="active-session-name" style="color: var(--ink-dim)">Select a conversation</span>
<span id="active-session-meta"></span>
</div>
</div>
<div id="supporter-messages">
<div class="empty-state">
<p>No conversation selected</p>
<p>Open the chat list and choose a conversation.</p>
</div>
</div>
<div id="supporter-peer-typing" class="typing-bubble" aria-live="polite">
<span class="typing-bubble-inner" aria-label="User is typing">
<span class="typing-dot"></span><span class="typing-dot"></span><span class="typing-dot"></span>
</span>
</div>
<div id="reply-area">
<input type="text" id="reply-input" placeholder="Reply to user..." autocomplete="off" maxlength="1000" />
<button id="reply-btn">Send</button>
</div>
</div>
</section>
</div>
<div id="drawer-backdrop" class="drawer-backdrop" onclick="closeSidebar()"></div> <div id="drawer-backdrop" class="drawer-backdrop" onclick="closeSidebar()"></div>
<aside id="sidebar" class="sidebar"> <!-- Mobile Menu Button (Hidden on Desktop) -->
<div class="sidebar-header"> <button class="mobile-menu-btn" onclick="openSidebar()" aria-label="Open conversations">Chats</button>
<span class="sidebar-title">Conversations</span>
<button class="close-sidebar" type="button" onclick="closeSidebar()">Close</button>
</div>
<div id="session-list"
hx-get="/sessions"
hx-trigger="load, refresh, every 5s"
hx-swap="innerHTML">
<p class="no-sessions">Loading...</p>
</div>
</aside>
<section class="main-panel"> <footer>
<div class="chat-shell"> <span>&copy; <script>document.write(new Date().getFullYear())</script> FLÓ.FO | ØLL RÆTTINDI TILSKILAÐ</span>
<div class="main-header"> <a href="https://www.facebook.com/people/FL%C3%93/61593006613799/" target="_blank" rel="noopener" class="footer-icon" aria-label="Facebook">
<div class="active-copy"> <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>
<span id="active-session-name" style="color: var(--ink-dim)">Select a conversation</span> </a>
<span id="active-session-meta"></span> <a href="https://git.flo.fo/FLO" target="_blank" rel="noopener" class="footer-icon" aria-label="Gitea">
</div> <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>
</div> </a>
<a href="https://www.linkedin.com/in/bartal-l%C3%A6arsson-63793895/" target="_blank" rel="noopener" class="footer-icon" aria-label="LinkedIn">
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>
</a>
</footer>
<div id="supporter-messages"> <button class="theme-toggle" id="theme-toggle" aria-label="Toggle theme"></button>
<div class="empty-state">
<p>No conversation selected</p>
<p>Open the chat list and choose a conversation.</p>
</div>
</div>
<div id="supporter-peer-typing" class="typing-bubble" aria-live="polite"> <script src="/static/js/shared.js"></script>
<span class="typing-bubble-inner" aria-label="User is typing"> <script src="/static/js/supporter.js"></script>
<span class="typing-dot"></span><span class="typing-dot"></span><span class="typing-dot"></span>
</span>
</div>
<div id="reply-area">
<input type="text" id="reply-input" placeholder="Reply to user..." autocomplete="off" maxlength="1000" />
<button id="reply-btn">Send</button>
</div>
</div>
</section>
</div>
<footer>
<span>&copy; <script>document.write(new Date().getFullYear())</script> FLÓ.FO | ØLL RÆTTINDI TILSKILAÐ</span>
<a href="https://www.facebook.com/people/FL%C3%93/61593006613799/" target="_blank" rel="noopener" class="footer-icon" aria-label="Facebook">
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>
</a>
<a href="https://git.flo.fo/FLO" target="_blank" rel="noopener" class="footer-icon" aria-label="Gitea">
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>
</a>
<a href="https://www.linkedin.com/in/bartal-l%C3%A6arsson-63793895/" target="_blank" rel="noopener" class="footer-icon" aria-label="LinkedIn">
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>
</a>
</footer>
<button class="theme-toggle" id="theme-toggle" aria-label="Toggle theme"></button>
<script src="/static/js/shared.js"></script>
<script src="/static/js/supporter.js"></script>
</body> </body>
</html> </html>
+7 -8
View File
@@ -22,14 +22,13 @@
</head> </head>
<body> <body>
<nav> <nav>
<a href="https://fló.fo" class="back-link">← FLÓ.FO</a> <a href="https://fló.fo" class="back-link">← FLÓ.FO</a>
<a href="/" class="nav-brand"> <a href="/" class="nav-brand">
FLÓ <span class="brand-sep">/</span> <span class="brand-sub">USER TO SUPPORT CHAT // RUST</span> FLÓ <span class="brand-sep">/</span> <span class="brand-sub">USER TO SUPPORT CHAT // RUST</span>
</a> </a>
</nav> <span class="nav-timer" hx-get="/reset-countdown" hx-trigger="load, every 1s" hx-swap="innerHTML">Reset in --:--</span>
</nav>
<span class="page-timer" hx-get="/reset-countdown" hx-trigger="load, every 1s" hx-swap="innerHTML">Reset in --:--</span>
<div class="chat-wrapper"> <div class="chat-wrapper">
<div id="chat-container"> <div id="chat-container">