| <script> |
| import { renderMarkdown, copyToClipboard } from '$lib/utils.js'; |
| import { showToast } from '$lib/stores.js'; |
| |
| export let message = { role: 'user', content: '', model: '', ts: null }; |
| export let streaming = false; |
| |
| $: isUser = message.role === 'user'; |
| $: html = isUser ? null : renderMarkdown(message.content); |
| |
| async function copy() { |
| await copyToClipboard(message.content); |
| showToast('Copied to clipboard', 'success', 2000); |
| } |
| </script> |
|
|
| <div class="flex {isUser ? 'justify-end' : 'justify-start'} gap-3 group animate-fade-in"> |
| {#if !isUser} |
| <div class="w-7 h-7 rounded-full bg-mac-800 border border-mac-700 flex items-center justify-center flex-shrink-0 mt-1"> |
| <span class="text-xs font-bold text-mac-300">M</span> |
| </div> |
| {/if} |
|
|
| <div class="max-w-[85%] min-w-0"> |
| |
| {#if isUser} |
| <div class="message-user"> |
| <p class="text-sm text-gray-100 whitespace-pre-wrap break-words">{message.content}</p> |
| </div> |
| {:else} |
| <div class="message-assistant"> |
| {#if streaming && !message.content} |
| |
| <div class="flex gap-1 py-1"> |
| <div class="typing-dot"></div> |
| <div class="typing-dot" style="animation-delay:150ms"></div> |
| <div class="typing-dot" style="animation-delay:300ms"></div> |
| </div> |
| {:else} |
| <div class="prose-sm text-gray-200 text-sm leading-relaxed break-words [&_pre]:mt-2 [&_code]:text-mac-300 [&_pre_code]:text-gray-200 [&_h1]:text-gray-100 [&_h2]:text-gray-100 [&_h3]:text-gray-200 [&_strong]:text-gray-100 [&_ul]:list-disc [&_ul]:pl-4 [&_ol]:list-decimal [&_ol]:pl-4"> |
| {@html html} |
| </div> |
| {/if} |
| </div> |
| {/if} |
|
|
| |
| <div class="flex items-center gap-2 mt-1 px-1 opacity-0 group-hover:opacity-100 transition-opacity"> |
| {#if message.model} |
| <span class="text-xs text-gray-600">{message.model}</span> |
| {/if} |
| {#if message.ts} |
| <span class="text-xs text-gray-600"> |
| {new Date(message.ts).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} |
| </span> |
| {/if} |
| {#if !isUser && message.content} |
| <button |
| on:click={copy} |
| class="text-xs text-gray-600 hover:text-gray-300 transition-colors ml-auto" |
| title="Copy" |
| > |
| 📋 |
| </button> |
| {/if} |
| </div> |
| </div> |
|
|
| {#if isUser} |
| <div class="w-7 h-7 rounded-full bg-dark-600 border border-dark-500 flex items-center justify-center flex-shrink-0 mt-1"> |
| <span class="text-xs text-gray-400">You</span> |
| </div> |
| {/if} |
| </div> |
|
|