File size: 5,586 Bytes
43d708a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
"use client"

import { useEffect, useRef } from "react"

type ChatMessage = {
  agent_id: string
  text: string
  type?: 'message' | 'death' | 'alliance_proposal' | 'alliance_accept' | 'alliance_reject' | 'leadership_vote' | 'leader_elected' | 'water_collected' | 'fire_extinguished'
  to_model?: string
  from_model?: string
  candidates?: string[]
}

export default function ChatFeed({ messages }: { messages: ChatMessage[] }) {
  const bottomRef = useRef<HTMLDivElement>(null)

  useEffect(() => {
    bottomRef.current?.scrollIntoView({ behavior: "smooth" })
  }, [messages])

  function getAgentName(id: string) {
    return id.split("/").at(-1)?.split("-")[0].toUpperCase() || id
  }

  function getAgentColor(name: string) {
    let hash = 0
    for (let i = 0; i < name.length; i++) {
      hash = name.charCodeAt(i) + ((hash << 5) - hash)
    }
    return `hsl(${hash % 360}, 70%, 60%)`
  }

  return (
    <div className="flex-1 overflow-y-auto py-4 space-y-3 min-h-0 custom-scrollbar">
      {messages.map((msg, i) => {
        const color = getAgentColor(msg.agent_id)
        
        if (msg.type === 'death') {
          return (
            <div key={i} className="mx-4 p-2 bg-red-500/10 border-l-2 border-red-500 animate-in slide-in-from-left duration-300">
              <span className="font-mono text-[10px] text-red-500 uppercase font-bold tracking-widest">
                πŸ’€ {getAgentName(msg.agent_id)} was consumed by the fire
              </span>
            </div>
          )
        }

        if (msg.type === 'alliance_proposal') {
            return (
              <div key={i} className="mx-4 p-2 bg-yellow-500/10 border-l-2 border-yellow-500 animate-in slide-in-from-left duration-300">
                <span className="font-mono text-[10px] text-yellow-500 uppercase font-bold tracking-widest">
                  🀝 {getAgentName(msg.agent_id)} proposed an alliance to {msg.to_model ? getAgentName(msg.to_model) : 'someone'}
                </span>
              </div>
            )
          }

        if (msg.type === 'alliance_accept') {
            return (
              <div key={i} className="mx-4 p-2 bg-emerald-500/10 border-l-2 border-emerald-500 animate-in slide-in-from-left duration-300">
                <span className="font-mono text-[10px] text-emerald-400 uppercase font-bold tracking-widest">
                  βœ… {getAgentName(msg.agent_id)} accepted the alliance{msg.to_model ? ` with ${getAgentName(msg.to_model)}` : ''}
                </span>
              </div>
            )
          }

        if (msg.type === 'alliance_reject') {
            return (
              <div key={i} className="mx-4 p-2 bg-orange-500/10 border-l-2 border-orange-500 animate-in slide-in-from-left duration-300">
                <span className="font-mono text-[10px] text-orange-400 uppercase font-bold tracking-widest">
                  ❌ {getAgentName(msg.agent_id)} rejected the alliance{msg.to_model ? ` from ${getAgentName(msg.to_model)}` : ''}
                </span>
              </div>
            )
          }

        if (msg.type === 'leadership_vote') {
            return (
              <div key={i} className="mx-4 p-2 bg-purple-500/10 border-l-2 border-purple-500 animate-in slide-in-from-left duration-300">
                <span className="font-mono text-[10px] text-purple-400 uppercase font-bold tracking-widest">
                  πŸ—³οΈ {getAgentName(msg.agent_id)} voted for {msg.candidates ? getAgentName(msg.candidates[0]) : 'someone'}
                </span>
              </div>
            )
          }

        if (msg.type === 'leader_elected') {
            return (
              <div key={i} className="mx-4 p-2 bg-yellow-500/10 border-l-2 border-yellow-500 animate-in slide-in-from-left duration-300">
                <span className="font-mono text-[10px] text-yellow-400 uppercase font-bold tracking-widest">
                  πŸ‘‘ {getAgentName(msg.agent_id)} elected as leader!
                </span>
              </div>
            )
          }

        if (msg.type === 'water_collected') {
            return (
              <div key={i} className="mx-4 p-2 bg-cyan-500/10 border-l-2 border-cyan-500 animate-in slide-in-from-left duration-300">
                <span className="font-mono text-[10px] text-cyan-400 uppercase font-bold tracking-widest">
                  πŸ’§ {getAgentName(msg.agent_id)} collected water!
                </span>
              </div>
            )
          }

        if (msg.type === 'fire_extinguished') {
            return (
              <div key={i} className="mx-4 p-2 bg-green-500/10 border-l-2 border-green-500 animate-in slide-in-from-left duration-300">
                <span className="font-mono text-[10px] text-green-400 uppercase font-bold tracking-widest">
                  πŸ”₯ Fire being extinguished! Intensity dropping...
                </span>
              </div>
            )
          }

        return (
          <div key={i} className="px-4 group animate-in fade-in duration-500">
            <div className="flex items-baseline gap-2">
              <span 
                className="font-mono text-[10px] font-bold shrink-0 px-1.5 rounded bg-white/5" 
                style={{ color }}
              >
                {getAgentName(msg.agent_id)}
              </span>
              <span className="font-mono text-[12px] text-white/80 leading-relaxed break-words">
                {msg.text}
              </span>
            </div>
          </div>
        )
      })}
      <div ref={bottomRef} />
    </div>
  )
}