Skip to main content

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.

Next up