mirror of
https://github.com/logos-messaging/lab.waku.org.git
synced 2026-01-07 08:13:10 +00:00
add history visualization partitioned and ordered by lamport timestamp
This commit is contained in:
parent
dc18ae685b
commit
283a0a389e
@ -206,7 +206,10 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
min-width: 400px;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.virtualizer-container {
|
||||
|
||||
@ -3,11 +3,15 @@
|
||||
import type { MessageChannelEventObject } from '$lib/sds/stream';
|
||||
import { getMessageId } from '$lib/sds/message';
|
||||
|
||||
export let event: MessageChannelEventObject;
|
||||
export let identicon: string;
|
||||
export let event: MessageChannelEventObject | undefined = undefined;
|
||||
export let identicon: string = '';
|
||||
export let currentIdFilter: string | null = null;
|
||||
export let onEventClick: (id: string | null) => void;
|
||||
export let onDependencyClick: (messageId: string, event: Event) => void;
|
||||
export let onEventClick: (id: string | null) => void = () => {};
|
||||
export let onDependencyClick: (messageId: string, event: Event) => void = () => {};
|
||||
export let width: number = 340;
|
||||
export let height: number = 178;
|
||||
|
||||
export let overflow: boolean = true;
|
||||
|
||||
// Map event types to colors using index signature
|
||||
const eventColors: { [key in string]: string } = {
|
||||
@ -29,13 +33,15 @@
|
||||
[MessageChannelEvent.MissedMessages]: 'Missed'
|
||||
};
|
||||
|
||||
$: id = getMessageId(event);
|
||||
$: color = eventColors[event.type] || '#888';
|
||||
$: name = eventNames[event.type] || event.type;
|
||||
$: id = event ? getMessageId(event) : null;
|
||||
$: color = event ? (eventColors[event.type] || '#888') : '#f0f0f0';
|
||||
$: name = event ? (eventNames[event.type] || event.type) : '';
|
||||
$: matchesFilter = currentIdFilter && id === currentIdFilter;
|
||||
|
||||
function handleEventClick() {
|
||||
onEventClick(id);
|
||||
if (event && id) {
|
||||
onEventClick(id);
|
||||
}
|
||||
}
|
||||
|
||||
function handleDependencyClick(messageId: string, e: Event) {
|
||||
@ -43,65 +49,75 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="history-item" on:click={handleEventClick}>
|
||||
<div class="item-container">
|
||||
<div
|
||||
class="event-box {matchesFilter ? 'highlight' : ''}"
|
||||
style="background-color: {color};"
|
||||
>
|
||||
<div class="identicon">
|
||||
<img src="data:image/svg+xml;base64,{identicon}" alt="Identicon" />
|
||||
<div class="history-item {!event ? 'empty' : ''}" style="width: 100%; height: {height}px;" on:click={event ? handleEventClick : undefined}>
|
||||
{#if event}
|
||||
<div class="item-container">
|
||||
<div
|
||||
class="event-box {matchesFilter ? 'highlight' : ''}"
|
||||
style="background-color: {color};"
|
||||
>
|
||||
<div class="identicon">
|
||||
<img src="data:image/svg+xml;base64,{identicon}" alt="Identicon" />
|
||||
</div>
|
||||
<div class="event-info" style="overflow: {overflow ? 'visible' : 'hidden'};">
|
||||
<div class="event-type">
|
||||
{name}
|
||||
</div>
|
||||
<div class="event-id">
|
||||
{id}
|
||||
</div>
|
||||
</div>
|
||||
{#if event.type === MessageChannelEvent.MessageDelivered}
|
||||
<div class="sent-or-received">
|
||||
{event.payload.sentOrReceived}
|
||||
</div>
|
||||
{/if}
|
||||
{#if event.type === MessageChannelEvent.MessageSent || event.type === MessageChannelEvent.MessageReceived}
|
||||
<div class="lamport-timestamp">
|
||||
{event.payload.lamportTimestamp}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="event-info">
|
||||
<div class="event-type">
|
||||
{name}
|
||||
</div>
|
||||
<div class="event-id">
|
||||
{id}
|
||||
</div>
|
||||
</div>
|
||||
{#if event.type === MessageChannelEvent.MessageDelivered}
|
||||
<div class="sent-or-received">
|
||||
{event.payload.sentOrReceived}
|
||||
</div>
|
||||
{/if}
|
||||
{#if event.type === MessageChannelEvent.MessageSent || event.type === MessageChannelEvent.MessageReceived}
|
||||
<div class="lamport-timestamp">
|
||||
{event.payload.lamportTimestamp}
|
||||
</div>
|
||||
{#each event.payload.causalHistory as dependency}
|
||||
{@const dependencyMatchesFilter =
|
||||
currentIdFilter && dependency.messageId === currentIdFilter}
|
||||
<div
|
||||
class="dependency-box {dependencyMatchesFilter ? 'highlight' : ''}"
|
||||
style="background-color: {color};"
|
||||
on:click={(e) => handleDependencyClick(dependency.messageId, e)}
|
||||
>
|
||||
{dependency.messageId}
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{#if event.type === MessageChannelEvent.MessageSent || event.type === MessageChannelEvent.MessageReceived}
|
||||
<!-- <section class="dependency-container"> -->
|
||||
{#each event.payload.causalHistory as dependency}
|
||||
{@const dependencyMatchesFilter =
|
||||
currentIdFilter && dependency.messageId === currentIdFilter}
|
||||
<div
|
||||
class="dependency-box {dependencyMatchesFilter ? 'highlight' : ''}"
|
||||
style="background-color: {color};"
|
||||
on:click={(e) => handleDependencyClick(dependency.messageId, e)}
|
||||
>
|
||||
{dependency.messageId}
|
||||
</div>
|
||||
{/each}
|
||||
<!-- </section> -->
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.history-item {
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.history-item:not(.empty) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.empty {
|
||||
border: 1px dashed #ccc;
|
||||
border-radius: 8px;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.item-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 6px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.event-box {
|
||||
@ -135,6 +151,8 @@
|
||||
color: white;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
transition: box-shadow 0.3s ease;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
|
||||
75
examples/sds-demo/src/lib/components/StateGraph.svelte
Normal file
75
examples/sds-demo/src/lib/components/StateGraph.svelte
Normal file
@ -0,0 +1,75 @@
|
||||
<script lang="ts">
|
||||
import type { MessageChannelEventObject } from '$lib/sds/stream';
|
||||
import { grid } from '$lib/utils/stateGraph.svelte';
|
||||
import HistoryItem from './HistoryItem.svelte';
|
||||
import { onMount, onDestroy, createEventDispatcher } from 'svelte';
|
||||
|
||||
// Props
|
||||
export let identicons: Array<string> = [];
|
||||
export let currentIdFilter: string | null = null;
|
||||
export let onEventClick: (id: string | null) => void;
|
||||
export let onDependencyClick: (messageId: string, event: Event) => void;
|
||||
export let columns: number = 10; // Default number of columns
|
||||
export let rows: number = 10; // Default number of rows
|
||||
|
||||
// Create 2D grid of items initialized with null (empty items)
|
||||
|
||||
onMount(() => {
|
||||
// Initialize the state graph stream
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
// Clean up if needed
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="state-graph" style="--columns: {columns};">
|
||||
{#each grid as row, y}
|
||||
{#each row as item, x}
|
||||
<div class="grid-item {item !== null ? 'filled' : ''}">
|
||||
{#if item !== null && y * columns + x < identicons.length}
|
||||
<HistoryItem
|
||||
event={item}
|
||||
identicon={identicons[y * columns + x]}
|
||||
{currentIdFilter}
|
||||
{onEventClick}
|
||||
{onDependencyClick}
|
||||
/>
|
||||
{:else}
|
||||
<HistoryItem />
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.state-graph {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--columns), 1fr);
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.grid-item {
|
||||
width: 340px;
|
||||
height: 178px;
|
||||
justify-self: center;
|
||||
}
|
||||
|
||||
.filled {
|
||||
animation: fadeIn 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.state-graph {
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
143
examples/sds-demo/src/lib/components/StateGraphDashboard.svelte
Normal file
143
examples/sds-demo/src/lib/components/StateGraphDashboard.svelte
Normal file
@ -0,0 +1,143 @@
|
||||
<script lang="ts">
|
||||
import { subscribeToAllEventsStream } from '$lib/sds/stream.svelte';
|
||||
import { MessageChannelEvent } from '@waku/sds';
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import { getIdenticon } from '$lib/identicon.svelte';
|
||||
import { getMessageId } from '$lib/sds/message';
|
||||
import type { MessageChannelEventObject } from '$lib/sds/stream';
|
||||
import { grid, recordMessage } from '$lib/utils/stateGraph.svelte';
|
||||
import { eventColors, eventNames } from '$lib/utils/event';
|
||||
import HistoryItem from './HistoryItem.svelte';
|
||||
// Store for history items
|
||||
let history: Array<MessageChannelEventObject> = $state([]);
|
||||
let identicons: {[messageId: string]: string} = $state({});
|
||||
let identicon: any = $state(null);
|
||||
|
||||
const minHeight = 32;
|
||||
const aspectRatio = 5.74;
|
||||
let containerWidth: number;
|
||||
let containerHeight: number;
|
||||
|
||||
const historyMutex = $state(false);
|
||||
|
||||
$effect(() => {
|
||||
// Update container dimensions when grid changes
|
||||
if (grid) {
|
||||
containerWidth = grid[0]?.length || 0;
|
||||
containerHeight = grid.length || 0;
|
||||
}
|
||||
});
|
||||
|
||||
// Unsubscribe function
|
||||
let unsubscribe: (() => void) | null = $state(null);
|
||||
|
||||
// let identicons = $derived(
|
||||
// identicon &&
|
||||
// history.map((event: MessageChannelEventObject) => {
|
||||
// const id = getMessageId(event);
|
||||
// return new identicon(id || '', { size: 40, format: 'svg' }).toString();
|
||||
// })
|
||||
// );
|
||||
|
||||
onMount(async () => {
|
||||
identicon = await getIdenticon();
|
||||
// Subscribe to the event stream and collect events
|
||||
unsubscribe = subscribeToAllEventsStream((event) => {
|
||||
if (event.type === MessageChannelEvent.MissedMessages) {
|
||||
return;
|
||||
}
|
||||
history = [event, ...history];
|
||||
recordMessage(event);
|
||||
const id = getMessageId(event);
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
if(identicons[id] === undefined) {
|
||||
identicons[id] = new identicon(id, { size: 40, format: 'svg' }).toString();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (unsubscribe) {
|
||||
unsubscribe();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="state-graph-container">
|
||||
{#if grid}
|
||||
<div class="grid-wrapper">
|
||||
<div
|
||||
class="grid"
|
||||
style="--cell-height: {minHeight}px; --cell-width: {minHeight *
|
||||
aspectRatio}px; --cols: {containerWidth}; --rows: {containerHeight}"
|
||||
>
|
||||
{#each grid as row}
|
||||
<div class="row">
|
||||
{#each row as cell}
|
||||
<!-- {#if cell}
|
||||
<div class="cell" style="background-color: {eventColors[cell.type]};">
|
||||
<div class="cell-content" color={eventColors[cell.type]}>
|
||||
{eventNames[cell.type]}
|
||||
</div>
|
||||
</div>
|
||||
{/if} -->
|
||||
{#if cell}
|
||||
{@const id = getMessageId(cell)}
|
||||
{#if id && identicons[id]}
|
||||
<HistoryItem overflow={false} height={minHeight} width={minHeight * aspectRatio} identicon={identicons[id]} event={cell} />
|
||||
{/if}
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.state-graph-container {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.grid-wrapper {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: flex;
|
||||
grid-template-columns: repeat(var(--cols), var(--cell-width));
|
||||
width: fit-content;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.cell {
|
||||
/* height: var(--cell-height);
|
||||
width: var(--cell-width); */
|
||||
max-width: var(--cell-width);
|
||||
max-height: var(--cell-height);
|
||||
border: 1px solid black;
|
||||
background-color: rgba(170, 165, 209, 0.79);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cell-content {
|
||||
font-size: 0.8rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
/* Remove unused styles */
|
||||
</style>
|
||||
@ -0,0 +1,84 @@
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import {
|
||||
actual_grid,
|
||||
update_virtual_grid,
|
||||
shift_actual_grid
|
||||
} from '$lib/utils/stateGraph.svelte';
|
||||
import { subscribeToAllEventsStream } from '$lib/sds/stream.svelte';
|
||||
import { MessageChannelEvent } from '@waku/sds';
|
||||
import { eventColors, eventNames } from '$lib/utils/event';
|
||||
onMount(() => {
|
||||
console.log('StateGraphSummary mounted');
|
||||
});
|
||||
|
||||
let unsubscribe: (() => void) | null = $state(null);
|
||||
|
||||
onMount(async () => {
|
||||
unsubscribe = subscribeToAllEventsStream((event) => {
|
||||
if (event.type === MessageChannelEvent.MissedMessages) {
|
||||
return;
|
||||
}
|
||||
update_virtual_grid(event);
|
||||
// shift_actual_grid();
|
||||
});
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (unsubscribe) {
|
||||
unsubscribe();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="summary-grid">
|
||||
{#each actual_grid as row}
|
||||
<div class="column mw-200 mr-2 rounded-lg bg-none p-5 sm:shadow-md">
|
||||
<p>{row.lamportTimestamp}</p>
|
||||
{#each row.columns as cell}
|
||||
{#if cell?.type}
|
||||
<div class="cell" style="background-color: {eventColors[cell.type]};">
|
||||
<p class="cell-text">{eventNames[cell.type]}</p>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.summary-grid {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
.cell {
|
||||
min-width: 100px;
|
||||
min-height: 50px;
|
||||
border: 1px solid black;
|
||||
margin: 1px;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.empty-cell {
|
||||
/* border: 1px solid black; */
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.column {
|
||||
border: 1px solid black;
|
||||
max-height: 400px;
|
||||
max-width: 280px;
|
||||
min-height: 200px;
|
||||
min-width: 200px;
|
||||
}
|
||||
.cell-text {
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
@ -73,7 +73,7 @@ async function send(payload: Uint8Array): Promise<void> {
|
||||
console.error('error sending message', result.failures);
|
||||
}
|
||||
return {
|
||||
success: result.successes.length > 0,
|
||||
success: true,
|
||||
retrievalHint: hash
|
||||
};
|
||||
});
|
||||
|
||||
20
examples/sds-demo/src/lib/utils/event.ts
Normal file
20
examples/sds-demo/src/lib/utils/event.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { MessageChannelEvent } from '@waku/sds';
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
// Event type to display name using index signature
|
||||
export 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'
|
||||
};
|
||||
123
examples/sds-demo/src/lib/utils/stateGraph.svelte.ts
Normal file
123
examples/sds-demo/src/lib/utils/stateGraph.svelte.ts
Normal file
@ -0,0 +1,123 @@
|
||||
import { type MessageChannelEventObject } from '$lib/sds/stream';
|
||||
import { MessageChannelEvent } from '@waku/sds';
|
||||
|
||||
const lamportTimestamp = $state(0);
|
||||
let maxLamportTimestamp = $state(0);
|
||||
|
||||
export const initializeGrid = (_maxLamportTimestamp: number) => {
|
||||
maxLamportTimestamp = _maxLamportTimestamp;
|
||||
const rows = maxLamportTimestamp;
|
||||
const columns = maxLamportTimestamp;
|
||||
return createGrid(rows, columns);
|
||||
};
|
||||
|
||||
export const addItems = (items: Array<MessageChannelEventObject>, _lamportTimestamp?: number) => {
|
||||
if (!_lamportTimestamp) {
|
||||
_lamportTimestamp = lamportTimestamp;
|
||||
}
|
||||
grid[_lamportTimestamp] = items;
|
||||
};
|
||||
|
||||
export const createGrid = (
|
||||
rows: number,
|
||||
columns: number
|
||||
): Array<Array<MessageChannelEventObject | null>> => {
|
||||
return Array(rows)
|
||||
.fill(null)
|
||||
.map(() => Array(columns).fill(null));
|
||||
};
|
||||
|
||||
type GridItem = {
|
||||
lamportTimestamp: number;
|
||||
events: Array<MessageChannelEventObject>;
|
||||
};
|
||||
|
||||
const x_start = $state(0);
|
||||
const x_window = 100;
|
||||
const x_threshold = 0;
|
||||
const virtual_grid: Map<number, GridItem> = $state(new Map());
|
||||
export const actual_grid = $state(createGrid(x_window, 10).map((row, index) => ({lamportTimestamp: index, columns: row})));
|
||||
export const update_virtual_grid = (event: MessageChannelEventObject) => {
|
||||
const lamportTimestamp = getLamportTimestamp(event);
|
||||
if (!lamportTimestamp) {
|
||||
return;
|
||||
}
|
||||
const events = virtual_grid.get(lamportTimestamp)?.events || [];
|
||||
events.push(event);
|
||||
virtual_grid.set(lamportTimestamp, { lamportTimestamp, events });
|
||||
if(lamportTimestamp > x_start + x_window - x_threshold) {
|
||||
shift_actual_grid(0 ,lamportTimestamp - x_window - x_threshold);
|
||||
} else if (x_start <= lamportTimestamp && lamportTimestamp <= x_start + x_window) {
|
||||
actual_grid[lamportTimestamp % x_window].columns.push(event);
|
||||
}
|
||||
}
|
||||
export const shift_actual_grid = (amount: number = 1, _x_start?: number) => {
|
||||
if (!_x_start) {
|
||||
_x_start = x_start;
|
||||
}
|
||||
for(let i = _x_start + amount; i < _x_start + x_window + amount; i++) {
|
||||
const events = virtual_grid.get(i)?.events || [];
|
||||
actual_grid[i % x_window] = {lamportTimestamp: i, columns: events};
|
||||
}
|
||||
}
|
||||
|
||||
export const grid = $state(createGrid(50, 10));
|
||||
|
||||
const getLamportTimestamp = (event: MessageChannelEventObject) => {
|
||||
let lamportTimestamp = null;
|
||||
if (
|
||||
event.type === MessageChannelEvent.MessageSent ||
|
||||
event.type === MessageChannelEvent.MessageReceived ||
|
||||
event.type === MessageChannelEvent.SyncSent ||
|
||||
event.type === MessageChannelEvent.SyncReceived
|
||||
) {
|
||||
lamportTimestamp = event.payload.lamportTimestamp;
|
||||
if (!lamportTimestamp) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
lamportTimestamp = longestTimestamp;
|
||||
}
|
||||
return lamportTimestamp;
|
||||
}
|
||||
const messagesPerLamportTimestamp = $state(new Map<number, Array<MessageChannelEventObject>>());
|
||||
let longestTimestamp = $state(0);
|
||||
export const recordMessage = (
|
||||
message: MessageChannelEventObject,
|
||||
_grid?: Array<Array<MessageChannelEventObject | null>>
|
||||
) => {
|
||||
if (!_grid) {
|
||||
_grid = grid;
|
||||
}
|
||||
let lamportTimestamp = null;
|
||||
if (
|
||||
message.type === MessageChannelEvent.MessageSent ||
|
||||
message.type === MessageChannelEvent.MessageReceived ||
|
||||
message.type === MessageChannelEvent.SyncSent ||
|
||||
message.type === MessageChannelEvent.SyncReceived
|
||||
) {
|
||||
lamportTimestamp = message.payload.lamportTimestamp;
|
||||
if (!lamportTimestamp) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
lamportTimestamp = longestTimestamp;
|
||||
}
|
||||
const messages = messagesPerLamportTimestamp.get(lamportTimestamp) || [];
|
||||
messages.push(message);
|
||||
messagesPerLamportTimestamp.set(lamportTimestamp, messages);
|
||||
if (lamportTimestamp > longestTimestamp) {
|
||||
longestTimestamp = lamportTimestamp;
|
||||
}
|
||||
const firstFill = _grid[lamportTimestamp].findIndex((item) => item !== null);
|
||||
if (firstFill === -1) {
|
||||
_grid[lamportTimestamp][Math.floor(_grid[lamportTimestamp].length / 2)] = message;
|
||||
} else {
|
||||
const lastFill = _grid[lamportTimestamp].findLastIndex((item) => item !== null);
|
||||
if (firstFill > _grid[lamportTimestamp].length - lastFill) {
|
||||
_grid[lamportTimestamp][firstFill - 1] = message;
|
||||
} else {
|
||||
_grid[lamportTimestamp][lastFill + 1] = message;
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -121,9 +121,8 @@ export async function startWaku(): Promise<void> {
|
||||
|
||||
// Connect to peers
|
||||
await node.dial(
|
||||
"/ip4/127.0.0.1/tcp/8000/ws/p2p/16Uiu2HAm6LgMnvadFttVeFsW5WHuoefsviCRbfo4AvnjySp4rnNt"
|
||||
// "/dns4/node-01.do-ams3.waku.sandbox.status.im/tcp/8095/wss/p2p/16Uiu2HAmNaeL4p3WEYzC9mgXBmBWSgWjPHRvatZTXnp8Jgv3iKsb"
|
||||
// '/dns4/waku-test.bloxy.one/tcp/8095/wss/p2p/16Uiu2HAmSZbDB7CusdRhgkD81VssRjQV5ZH13FbzCGcdnbbh6VwZ'
|
||||
"/ip4/127.0.0.1/tcp/8000/ws/p2p/16Uiu2HAm3TLea2NVs4dAqYM2gAgoV9CMKGeD1BkP3RAvmk7HBAbU"
|
||||
);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(window as any).waku = node;
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<ActionModule />
|
||||
<ConnectionIndicator />
|
||||
</Header>
|
||||
<div class="flex-1 overflow-auto">
|
||||
<div class="flex-1 overflow-auto my-1">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
// Redirect to history page when connected
|
||||
$effect(() => {
|
||||
if ($connectionState.status === "connected") {
|
||||
goto('/history');
|
||||
goto('/state-graph');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
42
examples/sds-demo/src/routes/state-graph/+page.svelte
Normal file
42
examples/sds-demo/src/routes/state-graph/+page.svelte
Normal file
@ -0,0 +1,42 @@
|
||||
<script lang="ts">
|
||||
import History from '$lib/components/History.svelte';
|
||||
import StateGraphDashboard from '$lib/components/StateGraphDashboard.svelte';
|
||||
import StateGraphSummary from '$lib/components/StateGraphSummary.svelte';
|
||||
</script>
|
||||
|
||||
<div class="mx-1 flex h-full flex-row" style="overflow: hidden;">
|
||||
<!-- 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;
|
||||
"
|
||||
>
|
||||
<History />
|
||||
</div>
|
||||
|
||||
<div class="flex w-full flex-col overflow-hidden">
|
||||
<!-- 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;
|
||||
"
|
||||
>
|
||||
<StateGraphSummary />
|
||||
</div>
|
||||
<!-- Detailed State Graph
|
||||
<div
|
||||
class="top-0 right-0 left-0 basis-1/4 rounded-lg bg-white p-1 sm:shadow-md"
|
||||
style="
|
||||
background-color: rgb(151 174 194);
|
||||
width: 100%;
|
||||
"
|
||||
>
|
||||
<StateGraphDashboard />
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
Loading…
x
Reference in New Issue
Block a user