File size: 2,408 Bytes
bc7e9cd
 
 
 
 
db9635c
bc7e9cd
 
 
 
 
 
db9635c
 
bc7e9cd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
db9635c
bc7e9cd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<script lang="ts">
  import type { ChatMessage } from "../../models/chat-data";
  import TextRenderer from "./TextRenderer.svelte";
  import ToolBlock from "./segments/ToolBlock.svelte";
  import TodoSegment from "./segments/TodoSegment.svelte";
  import { filterToolCalls } from "../../utils/tool-call-parser";

  export let message: ChatMessage;

  $: hasSegments = message.segments && message.segments.length > 0;
  $: isStreaming = message.streaming || (message.segments?.some(s => s.streaming));

  // Normalize content for consistent rendering with tool call filtering
  $: normalizedContent = hasSegments ? null : filterToolCalls(message.content || "");

  // Filter segments for rendering
  $: segmentsToRender = hasSegments ? message.segments!.filter((segment) => {
    // Skip tool results that are paired with previous invocation (except todos)
    if (segment.type === "tool-result") {
      const isTodoTool = segment.toolName?.includes("task");
      return isTodoTool;
    }
    return true;
  }) : [];
</script>

<div class="message-content">
  {#if normalizedContent !== null}
    <!-- Fallback content rendering -->
    <TextRenderer content={normalizedContent} streaming={isStreaming} />
    {#if isStreaming}
      <span class="streaming-indicator"></span>
    {/if}
  {:else if hasSegments}
    <!-- Segmented content rendering -->
    {#each segmentsToRender as segment (segment.id)}
      {#if segment.type === "text"}
        <TextRenderer content={filterToolCalls(segment.content)} streaming={segment.streaming || false} />
      {:else if segment.type === "tool-invocation"}
        <ToolBlock
          invocation={segment}
          result={message.segments && message.segments[message.segments.indexOf(segment) + 1]?.type === "tool-result"
            ? message.segments[message.segments.indexOf(segment) + 1]
            : null}
        />
      {:else if segment.type === "tool-result" && segment.toolName?.includes("task")}
        <TodoSegment {segment} />
      {/if}
    {/each}
  {/if}
</div>

<style>
  .message-content {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
  }

  .streaming-indicator {
    display: inline-block;
    color: rgba(65, 105, 225, 0.8);
    animation: pulse 1.5s ease-in-out infinite;
    margin-left: 0.25rem;
  }

  @keyframes pulse {
    0%, 100% {
      opacity: 0.3;
    }
    50% {
      opacity: 1;
    }
  }
</style>