Toast new events into the Event Log button
When the Event Log is collapsed, a freshly-arrived event now flashes as a toast anchored above the button, then animates down and fades into it. Suppressed on the initial state load and on rollback reseeds so it only fires for genuinely new events; clicking the toast opens the log. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -35,6 +35,11 @@
|
||||
let atBottom = true;
|
||||
let error = '';
|
||||
let timelineVersion = null;
|
||||
// Toast that briefly previews a freshly-arrived event while the log is
|
||||
// collapsed, then animates down into the Event Log button.
|
||||
let toast = null; // { id, message, kind }
|
||||
let initialized = false; // suppresses a toast flood on the first state load
|
||||
|
||||
|
||||
// Rollback buttons appear during a scene, for Admins and Deep players (server-enforced too).
|
||||
$: canRollback = state.game?.phase === 'scene'
|
||||
@@ -57,20 +62,24 @@
|
||||
// action from a rolled-back state truncates the abandoned future and bumps the
|
||||
// version, which tells us to drop what we'd accumulated and reseed.
|
||||
const version = state.game?.rollback_timeline_version ?? 0;
|
||||
let reset = false;
|
||||
if (timelineVersion !== null && version !== timelineVersion) {
|
||||
events = [];
|
||||
seenIds = new Set();
|
||||
reset = true;
|
||||
}
|
||||
timelineVersion = version;
|
||||
if (events.length === 0) {
|
||||
haveMore = state.events_have_more ?? polled.length >= PAGE_SIZE;
|
||||
}
|
||||
let added = false;
|
||||
let newest = null;
|
||||
for (const e of polled) {
|
||||
if (!seenIds.has(e.id)) {
|
||||
seenIds.add(e.id);
|
||||
events.push(e);
|
||||
added = true;
|
||||
newest = e;
|
||||
}
|
||||
}
|
||||
if (added) {
|
||||
@@ -79,7 +88,20 @@
|
||||
await tick();
|
||||
scrollToBottom();
|
||||
}
|
||||
// Toast only for genuinely new events (not the initial seed or a
|
||||
// rollback reseed) and only while the log is closed.
|
||||
if (initialized && !reset && !open && newest) {
|
||||
toast = { id: newest.id, message: newest.message, kind: newest.kind || 'info' };
|
||||
}
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
// Opening the log makes the preview redundant.
|
||||
$: if (open) toast = null;
|
||||
|
||||
function clearToast(id) {
|
||||
if (toast && toast.id === id) toast = null;
|
||||
}
|
||||
|
||||
function scrollToBottom() {
|
||||
@@ -149,6 +171,19 @@
|
||||
</script>
|
||||
|
||||
<div class="floating-event-log {open ? 'open' : ''}">
|
||||
{#if toast && !open}
|
||||
{#key toast.id}
|
||||
<button
|
||||
class="event-toast log-kind-{toast.kind}"
|
||||
title="Open the Event Log"
|
||||
on:click={toggleOpen}
|
||||
on:animationend={() => clearToast(toast.id)}
|
||||
>
|
||||
<span class="log-icon">{KIND_ICONS[toast.kind] || KIND_ICONS.info}</span>
|
||||
<span class="toast-message">{toast.message}</span>
|
||||
</button>
|
||||
{/key}
|
||||
{/if}
|
||||
<button class="toggle-log-btn" on:click={toggleOpen}>
|
||||
{open ? '⬇️ Hide Log' : '📜 Event Log'}
|
||||
</button>
|
||||
@@ -352,6 +387,48 @@
|
||||
filter: brightness(1.1);
|
||||
}
|
||||
|
||||
/* Toast preview of a fresh event, anchored above the button */
|
||||
.event-toast {
|
||||
position: absolute;
|
||||
bottom: calc(100% + 8px);
|
||||
right: 0;
|
||||
width: 320px;
|
||||
max-width: 80vw;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
text-align: left;
|
||||
padding: 10px 12px;
|
||||
background: color-mix(in srgb, var(--bg-deep) 95%, transparent);
|
||||
border: 1px solid var(--edge);
|
||||
border-left: 4px solid var(--accent);
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: var(--shadow-deep);
|
||||
backdrop-filter: blur(10px);
|
||||
color: var(--text);
|
||||
font-family: var(--font-body);
|
||||
cursor: pointer;
|
||||
transform-origin: bottom right;
|
||||
animation: event-toast-into-button 3.4s ease-in forwards;
|
||||
}
|
||||
.event-toast .toast-message {
|
||||
flex: 1;
|
||||
font-size: 0.85rem;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
@keyframes event-toast-into-button {
|
||||
0% { opacity: 0; transform: translateY(-6px) scale(0.96); }
|
||||
8% { opacity: 1; transform: translateY(0) scale(1); }
|
||||
75% { opacity: 1; transform: translateY(0) scale(1); }
|
||||
100% { opacity: 0; transform: translateY(40px) scale(0.4); }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.event-toast { animation-duration: 2.5s; animation-timing-function: linear; }
|
||||
}
|
||||
|
||||
/* Accent colors by event kind */
|
||||
.log-kind-challenge { border-left-color: var(--danger); }
|
||||
.log-kind-card { border-left-color: var(--deep); }
|
||||
|
||||
Reference in New Issue
Block a user