add a theme

This commit is contained in:
Arseniy Klempner 2025-04-03 18:56:12 -07:00
parent 03d92e9e7c
commit c8011d7a5b
No known key found for this signature in database
GPG Key ID: 51653F18863BD24B
5 changed files with 406 additions and 117 deletions

View File

@ -166,6 +166,37 @@
overflow-x: hidden;
min-width: 400px;
scrollbar-width: none;
background-color: #f8f3ff;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
position: relative;
border-left: 4px solid #9966CC;
border-right: 4px solid #9966CC;
padding: 12px;
}
.history-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 6px;
background: linear-gradient(to right, #9966CC, #F59E0B, #9966CC);
border-top-left-radius: 12px;
border-top-right-radius: 12px;
}
.history-container::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 6px;
background: linear-gradient(to right, #9966CC, #F59E0B, #9966CC);
border-bottom-left-radius: 12px;
border-bottom-right-radius: 12px;
}
.virtualizer-container {
@ -177,47 +208,78 @@
.header {
display: flex;
align-items: center;
padding: 8px;
padding: 8px 8px 16px 8px;
border-bottom: 1px solid rgba(107, 79, 138, 0.2);
margin-bottom: 8px;
position: relative;
}
.header::after {
content: '';
position: absolute;
left: 10%;
right: 10%;
bottom: -2px;
height: 2px;
background: linear-gradient(90deg, transparent, #F59E0B, transparent);
}
.help-button {
width: 28px;
height: 28px;
width: 32px;
height: 32px;
border-radius: 50%;
background-color: #f3f4f6;
border: 1px solid #d1d5db;
color: #4b5563;
background-color: #F59E0B;
border: 2px solid #FFC107;
color: white;
font-weight: bold;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
margin-right: 8px;
margin-right: 12px;
transition: all 0.2s;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.help-button:hover {
background-color: #e5e7eb;
color: #1f2937;
background-color: #DB8500;
transform: scale(1.05);
}
.item-filter {
flex: 1;
padding: 8px;
border-radius: 4px;
border: 1px solid #ddd;
padding: 10px 12px;
border-radius: 8px;
border: 2px solid #E0D0FF;
background-color: white;
font-size: 14px;
color: #6B4F8A;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
appearance: none;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236B4F8A' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right 12px center;
background-size: 16px;
transition: all 0.2s;
}
.item-filter:hover, .item-filter:focus {
border-color: #9966CC;
outline: none;
}
.id-filter-badge {
display: flex;
align-items: center;
background-color: #f3f4f6;
border-radius: 16px;
padding: 4px 12px;
margin: 8px;
background: linear-gradient(135deg, #F59E0B 0%, #F59E0B 100%);
border-radius: 20px;
padding: 6px 14px;
margin: 12px 8px;
max-width: fit-content;
font-size: 12px;
color: white;
box-shadow: 0 2px 6px rgba(245, 158, 11, 0.4);
}
.id-label {
@ -227,19 +289,29 @@
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: bold;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
.clear-filter-btn {
background: none;
background: rgba(255, 255, 255, 0.2);
border: none;
color: #6b7280;
color: white;
font-size: 16px;
font-weight: bold;
cursor: pointer;
padding: 0 4px;
padding: 0 6px;
border-radius: 50%;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.clear-filter-btn:hover {
color: #1f2937;
background: rgba(255, 255, 255, 0.4);
transform: scale(1.1);
}
</style>

View File

@ -2,6 +2,7 @@
import { MessageChannelEvent } from '@waku/sds';
import type { MessageChannelEventObject } from '$lib/sds/stream';
import { getMessageId } from '$lib/sds/message';
import { eventColors, eventNames } from '$lib/utils/event.svelte';
export let event: MessageChannelEventObject | undefined = undefined;
export let identicon: string = '';
@ -13,30 +14,6 @@
export let overflow: boolean = true;
// Map event types to colors using index signature
const eventColors: { [key in string]: string } = {
[MessageChannelEvent.MessageSent]: '#3B82F6', // blue
[MessageChannelEvent.MessageDelivered]: '#10B981', // green
[MessageChannelEvent.MessageReceived]: '#8B5CF6', // purple
[MessageChannelEvent.MessageAcknowledged]: '#059669', // dark green
[MessageChannelEvent.PartialAcknowledgement]: '#6D28D9', // dark purple
[MessageChannelEvent.MissedMessages]: '#EF4444', // red
[MessageChannelEvent.SyncSent]: '#F59E0B', // orange
[MessageChannelEvent.SyncReceived]: '#F59E0B' // dark orange
};
// Event type to display name using index signature
const eventNames: { [key in string]: string } = {
[MessageChannelEvent.MessageSent]: 'Sent',
[MessageChannelEvent.MessageDelivered]: 'Delivered',
[MessageChannelEvent.MessageReceived]: 'Received',
[MessageChannelEvent.MessageAcknowledged]: 'Acknowledged',
[MessageChannelEvent.PartialAcknowledgement]: 'Partially Acknowledged',
[MessageChannelEvent.MissedMessages]: 'Missed',
[MessageChannelEvent.SyncSent]: 'Sync Sent',
[MessageChannelEvent.SyncReceived]: 'Sync Received'
};
$: id = event ? getMessageId(event) : null;
$: color = event ? (eventColors[event.type] || '#888') : '#f0f0f0';
$: name = event ? (eventNames[event.type] || event.type) : '';
@ -103,6 +80,11 @@
.history-item {
padding: 8px;
box-sizing: border-box;
transition: transform 0.2s ease;
}
.history-item:not(.empty):hover {
transform: translateX(2px);
}
.history-item:not(.empty) {
@ -110,9 +92,9 @@
}
.empty {
border: 1px dashed #ccc;
border-radius: 8px;
background-color: #f9f9f9;
border: 1px dashed rgba(107, 79, 138, 0.2);
border-radius: 12px;
background-color: #f8f3ff;
}
.item-container {
@ -129,14 +111,27 @@
flex-direction: row;
align-items: center;
justify-content: flex-start;
border-radius: 8px;
border-radius: 12px;
width: 100%;
min-height: 70px;
color: white;
padding: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
padding: 12px;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);
position: relative;
transition: box-shadow 0.3s ease;
transition: all 0.3s ease;
border: none;
overflow: hidden;
}
.event-box::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(45deg, rgba(255,255,255,0.1), rgba(255,255,255,0));
z-index: 1;
}
.dependency-box {
@ -149,48 +144,76 @@
min-height: 40px;
font-size: 11px;
font-family: monospace;
opacity: 0.85;
padding: 6px 12px;
border-radius: 8px;
opacity: 0.9;
padding: 8px 14px;
border-radius: 10px;
color: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.3s ease;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);
transition: all 0.3s ease;
overflow: hidden;
text-overflow: ellipsis;
position: relative;
}
.dependency-box::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(45deg, rgba(255,255,255,0.1), rgba(255,255,255,0));
z-index: 1;
}
.highlight {
border-left: 4px solid white;
border-right: 4px solid white;
border-left: 4px solid #FFC107;
border-right: 4px solid #FFC107;
position: relative;
background-image: linear-gradient(rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.1));
animation: pulse 1.5s infinite;
}
.highlight .event-type {
font-size: 16px;
color: white;
font-weight: bold;
font-style: italic;
letter-spacing: 0.5px;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
.highlight .event-id,
.dependency-box.highlight {
font-weight: bold;
font-style: italic;
letter-spacing: 0.5px;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
.dependency-box.highlight {
font-size: 12px;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.7);
}
70% {
box-shadow: 0 0 0 6px rgba(255, 193, 7, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(255, 193, 7, 0);
}
}
.identicon {
width: 40px;
height: 40px;
border-radius: 4px;
border-radius: 8px;
overflow: hidden;
margin-right: 12px;
margin-right: 14px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
position: relative;
z-index: 2;
}
.identicon img {
@ -203,6 +226,8 @@
display: flex;
flex-direction: column;
align-items: flex-start;
position: relative;
z-index: 2;
}
.event-type {
@ -212,33 +237,42 @@
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
letter-spacing: 0.05em;
}
.event-id {
font-family: monospace;
font-size: 10px;
color: rgba(255, 255, 255, 0.7);
font-size: 11px;
color: rgba(255, 255, 255, 0.8);
max-width: 220px;
/* overflow: hidden; */
text-overflow: ellipsis;
white-space: nowrap;
}
.lamport-timestamp {
position: absolute;
top: 8px;
right: 12px;
top: 12px;
right: 14px;
font-size: 12px;
color: rgba(255, 255, 255, 0.9);
font-weight: 500;
background-color: rgba(0, 0, 0, 0.15);
padding: 3px 8px;
border-radius: 10px;
z-index: 2;
}
.sent-or-received {
position: absolute;
top: 8px;
right: 12px;
top: 12px;
right: 14px;
font-size: 12px;
color: rgba(255, 255, 255, 0.9);
font-weight: 500;
background-color: rgba(0, 0, 0, 0.15);
padding: 3px 8px;
border-radius: 10px;
z-index: 2;
}
</style>

View File

@ -38,13 +38,10 @@
{#each actual_grid as row}
{@const length = row.columns.filter((c) => c !== null).length}
{@const empty = 4 - length}
<div class="column mw-200 mr-2 mb-4 rounded-lg bg-none p-5 pt-0 sm:shadow-md">
<div class="flex flex-row items-center justify-between">
<p>{row.lamportTimestamp}</p>
<!-- <div class="flex items-center">
<div class="checkmark-large mr-1"></div>
<div class="checkmark-small"></div>
</div> -->
{@const isEmptyColumn = length === 0}
<div class="column mr-2 mb-4 rounded-lg p-4 pt-2 state-column {isEmptyColumn ? 'empty-column' : ''}">
<div class="lamport-timestamp">
<span>{row.lamportTimestamp}</span>
</div>
{#each row.columns as cell}
{@const filtered = currentIdFilter.id && cell && matchesIdFilter(cell)}
@ -72,34 +69,110 @@
flex-direction: row;
flex-wrap: wrap;
justify-content: space-evenly;
gap: 16px;
}
.lamport-timestamp {
text-align: center;
margin-bottom: 8px;
font-weight: bold;
font-size: 14px;
color: #6B4F8A;
position: relative;
}
.lamport-timestamp::before,
.lamport-timestamp::after {
content: '';
position: absolute;
height: 2px;
width: 30%;
background: linear-gradient(90deg, transparent, #F59E0B, transparent);
top: 50%;
}
.lamport-timestamp::before {
left: 5%;
}
.lamport-timestamp::after {
right: 5%;
}
.lamport-timestamp span {
background-color: #f8f3ff;
padding: 0 8px;
border-radius: 10px;
position: relative;
z-index: 1;
}
.cell {
min-width: 100px;
min-height: 50px;
border: 1px solid black;
margin: 1px;
align-content: center;
border: none;
margin: 4px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
position: relative;
overflow: hidden;
}
.cell::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(45deg, rgba(255,255,255,0.1), rgba(255,255,255,0));
z-index: 1;
}
.filtered {
box-shadow: 0 0 0 3px #FFC107;
animation: pulse 1.5s infinite;
}
.filtered-out {
opacity: 0.3;
opacity: 0.4;
}
.empty-cell {
/* border: 1px solid black; */
/* border: none !important; */
border: 1px dashed rgba(107, 79, 138, 0.2);
background-color: rgba(248, 243, 255, 0.5);
box-shadow: none;
}
.column {
border: 1px solid black;
border: none;
max-height: 400px;
max-width: 280px;
min-height: 200px;
min-width: 200px;
max-width: 280px;
background-color: #f8f3ff;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
border-radius: 12px;
position: relative;
overflow: hidden;
}
.state-column {
border-left: 4px solid #F59E0B;
border-right: 4px solid #F59E0B;
border-radius: 12px;
transition: transform 0.2s;
background-color: #f8f3ff;
}
.state-column:hover {
transform: translateY(-4px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
}
.cell-text {
font-size: 12px;
text-align: center;
@ -107,17 +180,36 @@
text-transform: uppercase;
letter-spacing: 0.1em;
color: white;
font-weight: bold;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
position: relative;
z-index: 2;
}
.checkmark-large {
font-size: 24px;
color: transparent;
-webkit-text-stroke: 1px black;
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.7);
}
70% {
box-shadow: 0 0 0 6px rgba(255, 193, 7, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(255, 193, 7, 0);
}
}
.checkmark-small {
font-size: 18px;
color: transparent;
-webkit-text-stroke: 1px black;
.empty-column {
background-color: rgba(107, 79, 138, 0.1);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
opacity: 0.8;
}
.empty-column .lamport-timestamp {
color: rgba(107, 79, 138, 0.6);
}
.empty-column .empty-cell {
border: 1px dashed rgba(107, 79, 138, 0.15);
background-color: rgba(248, 243, 255, 0.3);
}
</style>

View File

@ -3,14 +3,14 @@ import { MessageChannelEvent } from '@waku/sds';
import type { MessageChannelEventObject } from '$lib/sds/stream';
export const eventColors: { [key in string]: string } = {
[MessageChannelEvent.MessageSent]: '#3B82F6', // blue
[MessageChannelEvent.MessageDelivered]: '#10B981', // green
[MessageChannelEvent.MessageReceived]: '#8B5CF6', // purple
[MessageChannelEvent.MessageAcknowledged]: '#059669', // dark green
[MessageChannelEvent.PartialAcknowledgement]: '#6D28D9', // dark purple
[MessageChannelEvent.MissedMessages]: '#EF4444', // red
[MessageChannelEvent.SyncSent]: '#F59E0B', // orange
[MessageChannelEvent.SyncReceived]: '#F59E0B' // dark orange
[MessageChannelEvent.MessageSent]: '#427BF5', // bright blue
[MessageChannelEvent.MessageDelivered]: '#10B981', // vibrant green
[MessageChannelEvent.MessageReceived]: '#9966CC', // purple (inspired by image)
[MessageChannelEvent.MessageAcknowledged]: '#3F8C6F', // deeper green
[MessageChannelEvent.PartialAcknowledgement]: '#754FB0', // deep purple
[MessageChannelEvent.MissedMessages]: '#F06060', // coral red (from image)
[MessageChannelEvent.SyncSent]: '#F59E0B', // warm orange (from image)
[MessageChannelEvent.SyncReceived]: '#DB8500' // deeper orange
};
// Event type to display name using index signature

View File

@ -19,30 +19,121 @@
</script>
{#if match}
<div class="mx-1 flex h-full flex-row" style="overflow: hidden;">
<div class="main-container">
<!-- History Sidebar -->
<div
class="top-0 right-0 left-0 mr-1 rounded-lg bg-white sm:shadow-md"
style="
background-color: rgb(201 201 201);
display: flex;
"
>
<div class="history-panel">
<History channelId={match?.matchId ?? null} />
</div>
<div class="flex w-full flex-col overflow-hidden">
<div class="state-container">
<!-- Summary State Graph -->
<div
class="top-0 right-0 left-0 mb-1 h-full w-full flex-5 basis-3/4 rounded-lg bg-white p-8 sm:shadow-md"
style="
background-color: rgb(182 195 206);
align-content: center;
overflow: auto;
"
>
<div class="state-graph-panel">
<div class="panel-header">
<h2>State Synchronization Visualization</h2>
</div>
<StateGraphSummary channelId={match?.matchId ?? null} />
</div>
</div>
</div>
{/if}
<style>
.main-container {
display: flex;
flex-direction: row;
height: 100%;
padding: 16px;
gap: 16px;
overflow: hidden;
background: linear-gradient(135deg, #f0e6ff 0%, #f8f3ff 100%);
}
.history-panel {
flex: 0 0 400px;
display: flex;
}
.state-container {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
scrollbar-width: none;
}
.state-graph-panel {
height: 100%;
background-color: #f8f3ff;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
padding: 20px;
overflow: auto;
position: relative;
border-left: 4px solid #F59E0B;
border-right: 4px solid #F59E0B;
}
.state-graph-panel::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 6px;
background: linear-gradient(to right, #F59E0B, #9966CC, #F59E0B);
border-top-left-radius: 12px;
border-top-right-radius: 12px;
}
.state-graph-panel::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 6px;
background: linear-gradient(to right, #F59E0B, #9966CC, #F59E0B);
border-bottom-left-radius: 12px;
border-bottom-right-radius: 12px;
}
.panel-header {
margin-bottom: 20px;
text-align: center;
position: relative;
}
.panel-header h2 {
font-size: 20px;
color: #6B4F8A;
font-weight: 600;
margin: 0;
padding: 0 0 10px 0;
position: relative;
display: inline-block;
}
.panel-header h2::after {
content: '';
position: absolute;
bottom: 0;
left: 10%;
right: 10%;
height: 2px;
background: linear-gradient(90deg, transparent, #F59E0B, transparent);
}
@media (max-width: 1200px) {
.main-container {
flex-direction: column;
}
.history-panel {
flex: 0 0 300px;
}
.state-container {
flex: 1;
}
}
</style>