trusty-memory 0.1.45

Machine-wide, blazingly fast AI memory service
<script>
  /*
   * Why: Chat threads need consistent bubble styling for user vs assistant
   * messages, with a streaming indicator while tokens arrive over SSE.
   * What: Renders a single message bubble. User bubbles are right-aligned
   * and accent-colored; assistant bubbles are left-aligned and gray. A
   * blinking cursor appears at the end of assistant bubbles while streaming.
   * Test: Render with role='user' and confirm right alignment; render with
   * role='assistant' streaming=true and confirm cursor blinks after content.
   */
  let { role, content, streaming = false } = $props();

  let isUser = $derived(role === 'user');
</script>

<div class="row" class:row-user={isUser}>
  <div class="bubble" class:bubble-user={isUser} class:bubble-assistant={!isUser}>
    <div class="role">{role}</div>
    <div class="body">{content}{#if streaming && !isUser}<span class="cursor"></span>{/if}</div>
  </div>
</div>

<style>
  .row {
    display: flex;
    justify-content: flex-start;
    margin-bottom: var(--trusty-space-3);
  }
  .row-user {
    justify-content: flex-end;
  }
  .bubble {
    max-width: 78%;
    padding: var(--trusty-space-3) var(--trusty-space-4);
    border-radius: var(--trusty-radius-lg);
    font-size: var(--trusty-fs-sm);
    line-height: 1.55;
    box-shadow: var(--trusty-shadow-sm);
  }
  .bubble-user {
    background: var(--trusty-accent);
    color: var(--trusty-text-inverse);
    border-bottom-right-radius: var(--trusty-radius-sm);
  }
  .bubble-assistant {
    background: var(--trusty-card-bg);
    color: var(--trusty-text-primary);
    border: 1px solid var(--trusty-border);
    border-bottom-left-radius: var(--trusty-radius-sm);
  }
  .role {
    font-size: var(--trusty-fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
    opacity: 0.75;
    margin-bottom: 4px;
  }
  .bubble-user .role {
    color: rgba(255, 255, 255, 0.85);
  }
  .bubble-assistant .role {
    color: var(--trusty-text-muted);
  }
  .body {
    white-space: pre-wrap;
    word-wrap: break-word;
  }
  .cursor {
    display: inline-block;
    width: 7px;
    height: 1em;
    margin-left: 2px;
    vertical-align: text-bottom;
    background: currentColor;
    opacity: 0.7;
    animation: blink 1s steps(2, start) infinite;
  }
  @keyframes blink {
    to {
      visibility: hidden;
    }
  }
</style>