File size: 4,802 Bytes
fc851c4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
document.addEventListener('DOMContentLoaded', () => {
    const startBtn = document.getElementById('startBtn');
    const topicInput = document.getElementById('topicInput');
    const arena = document.getElementById('arena');
    const globalStatus = document.getElementById('globalStatus');
    const modelStatusList = document.getElementById('modelStatusList');

    const MODELS = [
        'openai/gpt-oss-120b',
        'llama-3.3-70b-versatile',
        'qwen/qwen3-32b',
        'gemini-2.5-flash',
        'Gemma 4 (Architect)'
    ];

    function initSidebar() {
        modelStatusList.innerHTML = '';
        MODELS.forEach(m => {
            const li = document.createElement('li');
            li.id = `status-${m.replace(/[^a-zA-Z0-9]/g, '')}`;
            li.innerHTML = `
                <span class="model-name">${m}</span>
                <span class="model-status">Awaiting task</span>
            `;
            modelStatusList.appendChild(li);
        });
    }

    initSidebar();

    startBtn.addEventListener('click', async () => {
        const topic = topicInput.value.trim();
        if (!topic) return;

        startBtn.disabled = true;
        arena.innerHTML = '';
        initSidebar();

        try {
            const response = await fetch('/api/debate', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ topic })
            });

            const reader = response.body.getReader();
            const decoder = new TextDecoder("utf-8");

            let buffer = "";

            while (true) {
                const { done, value } = await reader.read();
                if (done) break;

                buffer += decoder.decode(value, { stream: true });
                const lines = buffer.split('\n\n');
                
                buffer = lines.pop() || "";

                for (const chunk of lines) {
                    if (chunk.trim() === '') continue;
                    
                    const eventMatch = chunk.match(/event: (.*)\n/);
                    const dataMatch = chunk.match(/data: (.*)/);

                    if (eventMatch && dataMatch) {
                        const event = eventMatch[1];
                        const data = JSON.parse(dataMatch[1]);
                        handleEvent(event, data);
                    }
                }
            }
        } catch (error) {
            console.error(error);
            globalStatus.innerText = "Error occurred.";
        } finally {
            startBtn.disabled = false;
        }
    });

    function handleEvent(event, data) {
        if (event === 'status') {
            globalStatus.innerText = data.message;
        } 
        else if (event === 'model_start') {
            const id = `status-${data.model.replace(/[^a-zA-Z0-9]/g, '')}`;
            const el = document.getElementById(id);
            if (el) {
                el.className = 'active';
                el.querySelector('.model-status').innerHTML = `<span class="pulse-text">Thinking (Round ${data.round})...</span>`;
            }
        }
        else if (event === 'model_end') {
            const id = `status-${data.model.replace(/[^a-zA-Z0-9]/g, '')}`;
            const el = document.getElementById(id);
            if (el) {
                el.className = 'done';
                el.querySelector('.model-status').innerText = `Finished Round ${data.round}`;
            }
            appendMessage(data.model, data.round, data.content);
        }
        else if (event === 'done') {
            globalStatus.innerText = "Debate Concluded. Final Verdict Delivered.";
            globalStatus.classList.remove('pulse-text');
            MODELS.forEach(m => {
                const id = `status-${m.replace(/[^a-zA-Z0-9]/g, '')}`;
                const el = document.getElementById(id);
                if (el) { el.classList.remove('active'); el.classList.remove('done'); }
            });
        }
    }

    function appendMessage(model, round, markdownContent) {
        const div = document.createElement('div');
        div.className = `message-bubble ${model.includes('Architect') ? 'architect' : ''}`;
        div.style.animationDelay = `${0.1 * document.querySelectorAll('.message-bubble').length}s`;
        
        let roundLabel = round === 'Final' ? 'VERDICT' : `Round ${round}`;
        
        div.innerHTML = `
            <div class="bubble-header">
                <span>${model}</span>
                <span class="round">${roundLabel}</span>
            </div>
            <div class="bubble-content">
                ${marked.parse(markdownContent)}
            </div>
        `;
        arena.appendChild(div);
        arena.scrollTop = arena.scrollHeight;
    }
});