109 lines
3.5 KiB
Svelte
109 lines
3.5 KiB
Svelte
<script lang="ts">
|
|
import type { ContextualChatEntry } from '$lib/game-chat.js';
|
|
import { timeFormat } from '$ui/stores/time-zone.js';
|
|
import Badge from '../primitives/Badge.svelte';
|
|
|
|
let {
|
|
entries,
|
|
selectedMessageIds = [],
|
|
highlightedMessageIds = []
|
|
}: {
|
|
entries: ContextualChatEntry[];
|
|
selectedMessageIds?: string[];
|
|
highlightedMessageIds?: string[];
|
|
} = $props();
|
|
</script>
|
|
|
|
<p class="excerpt-note">
|
|
Selected messages and surrounding context in archive order. Messages between separate selections
|
|
may be omitted.
|
|
</p>
|
|
<ol class="excerpt" aria-label="Preserved conversation excerpt">
|
|
{#each entries as entry}
|
|
{#if entry.type === 'message'}
|
|
{@const highlighted =
|
|
!!entry.messageId && highlightedMessageIds.includes(entry.messageId)}
|
|
{@const selected = !!entry.messageId && selectedMessageIds.includes(entry.messageId)}
|
|
<li class:highlighted>
|
|
<div class="author">
|
|
<strong>{entry.message.player}</strong>
|
|
{#if entry.message.channel === 'team'}<Badge>Team chat</Badge>{/if}
|
|
{#if highlighted}<Badge variant="warning">Subject message</Badge>
|
|
{:else if selected}<Badge>Selected context</Badge>
|
|
{:else}<span>Surrounding context</span>{/if}
|
|
</div>
|
|
<div class="metadata">
|
|
<span>GUID {entry.message.guid}</span>
|
|
{#if entry.context.realTime}<span
|
|
>Estimated time <time datetime={entry.context.realTime}
|
|
>{$timeFormat.dateTime(entry.context.realTime)}</time
|
|
></span
|
|
>{:else}<span>Message time unknown</span>{/if}
|
|
<span>Log {entry.message.time}</span>
|
|
{#if entry.context.round !== null}<span>Round {entry.context.round}</span>{/if}
|
|
</div>
|
|
<p>{entry.message.message}</p>
|
|
{#if entry.messageId}<details>
|
|
<summary>Message identifier</summary><code>{entry.messageId}</code>
|
|
</details>{/if}
|
|
</li>
|
|
{/if}
|
|
{/each}
|
|
</ol>
|
|
|
|
<style>
|
|
.excerpt {
|
|
display: grid;
|
|
gap: var(--space-2);
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
li {
|
|
padding: var(--space-3);
|
|
border-left: 3px solid var(--border-subtle);
|
|
background: var(--surface-sunken);
|
|
min-width: 0;
|
|
}
|
|
li.highlighted {
|
|
border-left-color: var(--state-warning);
|
|
background: var(--surface-raised);
|
|
}
|
|
.author,
|
|
.metadata {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: var(--space-2);
|
|
overflow-wrap: anywhere;
|
|
}
|
|
.author > span,
|
|
.metadata {
|
|
color: var(--text-secondary);
|
|
font-size: var(--text-xs);
|
|
}
|
|
.metadata {
|
|
margin-top: var(--space-1);
|
|
}
|
|
p {
|
|
white-space: pre-wrap;
|
|
overflow-wrap: anywhere;
|
|
margin: var(--space-2) 0 0;
|
|
line-height: var(--leading-body);
|
|
}
|
|
details {
|
|
margin-top: var(--space-2);
|
|
font-size: var(--text-xs);
|
|
color: var(--text-secondary);
|
|
overflow-wrap: anywhere;
|
|
}
|
|
summary {
|
|
cursor: pointer;
|
|
}
|
|
.excerpt-note {
|
|
margin: 0;
|
|
font-size: var(--text-xs);
|
|
color: var(--text-secondary);
|
|
}
|
|
</style>
|