v3: added a chatbox
Some checks failed
Build and Publish Docker Image / build-and-push (push) Has been cancelled

This commit is contained in:
khannurien
2026-06-29 20:25:10 +00:00
parent f171027673
commit 6c4f410d9b
35 changed files with 1800 additions and 152 deletions

View File

@@ -1162,6 +1162,75 @@ body.has-fab .page-content {
padding-bottom: 5rem;
}
/* Chat FAB — mirrors .dump-fab and stacks directly above it (the "+" dump
button keeps the bottom slot; chat sits one button-height up) so the two
never overlap. */
.chat-fab {
position: fixed;
--fab-size: 3.5rem;
/* Share the dump-fab's column. */
right: max(1.25rem, calc(50% - var(--fab-lane, 860px) / 2 - var(--fab-size) - 1rem));
/* One button-height + gap above the dump-fab's bottom-anchored resting spot. */
top: calc(100vh - var(--fab-size) * 2 - 2rem - env(safe-area-inset-bottom, 0px));
top: calc(100dvh - var(--fab-size) * 2 - 2rem - env(safe-area-inset-bottom, 0px));
z-index: 900;
width: var(--fab-size);
height: var(--fab-size);
display: flex;
align-items: center;
justify-content: center;
padding: 0;
border: none;
border-radius: var(--fab-radius, 50%);
background: var(--color-accent);
color: var(--color-on-accent);
cursor: pointer;
box-shadow: 0 4px 16px color-mix(in srgb, var(--color-accent) 45%, transparent),
0 2px 6px rgba(0, 0, 0, 0.3);
opacity: 0;
transform: translateY(1rem) scale(0.9);
pointer-events: none;
transition: opacity 0.2s ease, transform 0.2s ease, background 0.15s ease,
top 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.chat-fab-icon {
width: 1.6rem;
height: 1.6rem;
}
.chat-fab--visible {
opacity: 1;
transform: translateY(0) scale(1);
pointer-events: auto;
}
.chat-fab:hover {
background: var(--color-accent-hover);
transform: translateY(-2px) scale(1);
box-shadow: 0 6px 20px color-mix(in srgb, var(--color-accent) 55%, transparent),
0 2px 6px rgba(0, 0, 0, 0.3);
}
.chat-fab:active {
transform: translateY(0) scale(0.96);
}
/* Match the dump-fab's clearance shift when a player is mounted. */
body.has-player .chat-fab {
top: calc(1.25rem + env(safe-area-inset-top, 0px));
/* Stack below the dump-fab in the top-right corner to avoid overlap. */
right: max(1.25rem, calc(50% - var(--fab-lane, 860px) / 2 - var(--fab-size) - 1rem));
top: calc(1.25rem + var(--fab-size) + 0.75rem + env(safe-area-inset-top, 0px));
}
/* The badge sits on the circular FAB; nudge it onto the rim. */
.chat-fab .notification-badge {
top: 2px;
right: 2px;
box-shadow: 0 0 0 2px var(--color-accent);
}
.rich-content-thumbnail-btn {
position: relative;
padding: 0;
@@ -2148,6 +2217,7 @@ body.has-fab .page-content {
/* Icon-only controls are square, so the row reads as a tidy set. */
.app-header-nav .nav-search-btn,
.app-header-nav .notification-bell,
.app-header-nav .chat-button,
.app-header-nav .user-menu-trigger {
width: 2.25rem;
padding: 0;
@@ -3221,6 +3291,255 @@ body.has-fab .page-content {
gap: 0.75rem;
}
/* ── Live chat ── */
.chat {
display: flex;
flex-direction: column;
flex: 1 1 auto;
min-height: 0;
height: min(60vh, 480px);
gap: 0.75rem;
}
.chat-messages {
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
/* Reserve a gutter for the scrollbar so it never sits on top of the
messages — without this, overlay scrollbars overlap the content. */
scrollbar-gutter: stable;
display: flex;
flex-direction: column;
gap: 0.85rem;
padding-right: 0.5rem;
}
.chat-empty {
margin: auto 0;
text-align: center;
color: var(--color-text-muted);
}
.chat-load-older {
text-align: center;
padding-bottom: 0.25rem;
}
.chat-message {
display: flex;
gap: 0.6rem;
align-items: flex-start;
/* Each message animates once, when its element first mounts — so newly
arriving messages slide in while existing ones stay put (React reuses
keyed DOM nodes). */
animation: chat-msg-in 0.18s ease-out;
}
@keyframes chat-msg-in {
from {
opacity: 0;
transform: translateY(6px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (prefers-reduced-motion: reduce) {
.chat-message {
animation: none;
}
}
.chat-message-body {
flex: 1;
min-width: 0;
}
.chat-message-meta {
display: flex;
align-items: baseline;
flex-wrap: wrap;
gap: 0.5rem;
margin-bottom: 0.1rem;
}
.chat-message-author {
font-weight: 700;
font-size: 0.85rem;
color: inherit;
text-decoration: none;
}
.chat-message-author:hover {
color: var(--color-accent);
}
.chat-message-time {
font-size: 0.7rem;
color: var(--color-text-muted);
}
.chat-message-text {
font-size: 0.9rem;
}
.chat-message-text.md {
line-height: 1.45;
}
.chat-message-edited {
font-size: 0.72rem;
color: var(--color-text-muted);
opacity: 0.7;
font-style: italic;
}
/* Edit/Delete actions: bare inline icons that follow the author/time in the
meta row, sitting right next to the message they belong to. Faded in on hover
(or focus, for keyboard use) so they stay unobtrusive. */
.chat-message-actions {
margin-left: 0.35rem;
display: inline-flex;
gap: 0.15rem;
opacity: 0;
transition: opacity 0.15s;
}
.chat-message:hover .chat-message-actions,
.chat-message:focus-within .chat-message-actions {
opacity: 1;
}
@media (hover: none) {
/* No hover on touch devices — keep the actions visible. */
.chat-message-actions {
opacity: 1;
}
}
.chat-icon-btn {
background: none;
border: none;
padding: 0 0.1rem;
font-size: 0.8rem;
line-height: 1;
color: var(--color-text-muted);
cursor: pointer;
transition: color 0.15s;
}
.chat-icon-btn:hover {
color: var(--color-text);
}
.chat-icon-btn--delete:hover {
color: var(--color-danger);
}
.chat-edit {
display: flex;
flex-direction: column;
gap: 0.4rem;
}
.chat-edit-actions {
display: flex;
justify-content: flex-end;
gap: 0.4rem;
}
/* Compact buttons for the inline edit box — the full-size form buttons are too
bulky for the narrow message column. */
.chat-edit-actions .btn-primary,
.chat-edit-actions .btn-secondary {
font-size: 0.8rem;
padding: 0.25rem 0.7rem;
box-shadow: none;
}
.chat-composer {
display: flex;
gap: 0.5rem;
align-items: flex-end;
flex-shrink: 0;
}
.chat-composer .mention-textarea-wrap {
flex: 1 1 auto;
min-width: 0;
}
/* Reserve room on the input's bottom-right for the char counter. `display:
block` removes the inline-block baseline descender gap below the textarea —
that phantom space was making the wrapper taller than the textarea, so the
bottom-aligned Send button landed a few px low. */
.chat-input {
padding-right: 3.5rem;
display: block;
}
/* Pin the counter inside the textarea instead of letting it add a row below —
that extra height is what knocked the Send button out of alignment. */
.chat-composer .text-editor-count {
position: absolute;
right: 0.55rem;
bottom: 0.4rem;
margin: 0;
}
.chat-send-btn {
flex-shrink: 0;
/* Match the input's resting height so the two line up exactly (both edges),
while the input is free to grow taller as you type. */
height: 2.4rem;
box-sizing: border-box;
}
/* The composer's popovers (mention list, emoji picker) anchor to the input,
which sits at the very bottom of the modal. The modal body must not clip them
— let them escape the body entirely so they're never cropped. The message
list keeps its own scroll, so nothing else relies on the body clipping. */
.modal-card:has(.chat) .modal-body {
overflow: visible;
}
/* Open the mention list upward, into the roomy message area above the input. */
.chat-composer .mention-dropdown {
top: auto;
bottom: 100%;
margin: 0 0 4px;
}
/* Very narrow viewports: stack the input and Send button. */
@media (max-width: 400px) {
.chat-composer {
flex-direction: column;
align-items: stretch;
}
.chat-send-btn {
height: auto;
width: 100%;
}
/* The author/time/edited row gets cramped when squeezed side-by-side here —
stack the metadata instead so each piece stays readable. Keep the stacked
lines tight so they don't balloon the message height. */
.chat-message-meta {
flex-direction: column;
align-items: flex-start;
gap: 0;
line-height: 1.25;
}
/* No longer trailing the time inline; align it with the stacked metadata. */
.chat-message-actions {
margin-left: 0;
}
}
.confirm-modal-message {
margin: 0;
font-size: 0.95rem;
@@ -4359,6 +4678,27 @@ body.has-fab .page-content {
box-shadow: 0 0 0 2px var(--color-bg);
}
/* ── Header chat button ── */
.chat-button {
position: relative;
background: var(--color-header-user-bg);
border: none;
cursor: pointer;
font-size: 0.95rem;
font-weight: 600;
padding: 0.35rem 0.85rem;
border-radius: 8px;
transition: background 0.15s;
display: inline-flex;
align-items: center;
}
.chat-button:hover {
background: var(--color-header-user-bg-hover);
}
.chat-button-icon {
display: inline-block;
}
/* ── Notifications page ── */
.notifications-page {
max-width: 680px;