MessagesState
Per-ticket message stream. Subscribes to realtime updates while the instance is alive. Pair with TypingState for the indicators.
Usage
src/lib/components/Thread.svelte
<script>
import { onDestroy } from 'svelte';
import { MessagesState, TypingState } from '@cstar.help/svelte';
let { ticketId } = $props();
const messages = new MessagesState(undefined, ticketId);
const typing = new TypingState(undefined, ticketId);
let draft = $state('');
async function handleSend() {
await messages.send(draft);
draft = '';
}
onDestroy(() => {
messages.destroy();
typing.destroy();
});
</script>
{#each messages.messages as m (m.id)}
<p><strong>{m.sender_name}:</strong> {m.content}</p>
{/each}
{#if typing.agents.length > 0}
<p class="typing">{typing.agents.map((a) => a.name).join(', ')} typing…</p>
{/if}
<input bind:value={draft} />
<button onclick={handleSend}>Send</button>Why ticketId is positional
ticketId is the second positional argument so the type system can require it. The
state class throws a typed TypeError at construction if you accidentally pass a thunk
(a common Svelte 5 mistake) instead of a string — much clearer than a silent URL encoding mishap.