Skip to content

Commit a1af0b4

Browse files
committed
Enh(viewer): Always show summary of players
1 parent b9e01a0 commit a1af0b4

5 files changed

Lines changed: 244 additions & 9 deletions

File tree

codeclash/games/abstract.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def __init__(self, config: dict):
2727
self.url_gh: str = f"git@github.com:{GH_ORG}/{self.name}.git"
2828
self.artifacts: list[Path] = []
2929
self.scoreboard: list[tuple[int, str]] = []
30+
"""List of (round number, winner (player id))"""
3031
self.config: dict = config["game"]
3132
self.rounds: int = self.config.get("rounds", 1)
3233
self.round: int = 0

codeclash/viewer/static/css/style.css

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,78 @@ body {
220220
margin-bottom: 1rem;
221221
}
222222

223+
.trajectory-header {
224+
background-color: var(--bg-tertiary);
225+
border-radius: 0.5rem;
226+
padding: 1rem;
227+
margin-bottom: 0.5rem;
228+
border: 1px solid var(--border-color);
229+
}
230+
231+
.trajectory-header h3 {
232+
color: var(--success-color);
233+
font-size: 1.125rem;
234+
font-weight: 600;
235+
margin-bottom: 0.75rem;
236+
}
237+
238+
.trajectory-messages-foldout {
239+
margin-top: 1rem;
240+
border: 1px solid var(--border-color);
241+
border-radius: 0.375rem;
242+
background-color: var(--bg-secondary);
243+
overflow: hidden;
244+
}
245+
246+
.trajectory-messages-foldout summary {
247+
padding: 0.75rem;
248+
cursor: pointer;
249+
font-weight: 500;
250+
background-color: var(--bg-primary);
251+
border-bottom: 1px solid var(--border-color);
252+
user-select: none;
253+
transition: background-color 0.2s ease;
254+
font-size: 0.875rem;
255+
}
256+
257+
.trajectory-messages-foldout summary:hover {
258+
background-color: var(--accent-color);
259+
color: white;
260+
}
261+
262+
.trajectory-messages-foldout[open] summary {
263+
border-bottom: 1px solid var(--border-color);
264+
}
265+
266+
.trajectory-messages-foldout .trajectory-content {
267+
padding: 1rem;
268+
}
269+
270+
.collapse-messages-button {
271+
margin-top: 1rem;
272+
text-align: center;
273+
padding-top: 1rem;
274+
border-top: 1px solid var(--border-color);
275+
}
276+
277+
.btn-collapse-messages {
278+
background-color: var(--bg-tertiary);
279+
border: 1px solid var(--border-color);
280+
border-radius: 0.375rem;
281+
padding: 0.5rem 1rem;
282+
cursor: pointer;
283+
font-size: 0.875rem;
284+
color: var(--text-primary);
285+
transition: all 0.2s ease;
286+
font-weight: 500;
287+
}
288+
289+
.btn-collapse-messages:hover {
290+
background-color: var(--accent-color);
291+
color: white;
292+
border-color: var(--accent-color);
293+
}
294+
223295
.trajectory-stats {
224296
display: flex;
225297
gap: 2rem;

codeclash/viewer/static/js/app.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,27 @@ function collapseMessage(clickedElement) {
218218
}, 100);
219219
}
220220

221+
function collapseTrajectoryMessages(clickedElement) {
222+
// Find the parent trajectory messages foldout
223+
const trajectoryFoldout = clickedElement.closest('.trajectory-messages-foldout');
224+
225+
if (trajectoryFoldout) {
226+
// Close the details element
227+
trajectoryFoldout.removeAttribute('open');
228+
229+
// Smooth scroll to the trajectory header
230+
setTimeout(() => {
231+
const trajectoryHeader = trajectoryFoldout.closest('.trajectory-header');
232+
if (trajectoryHeader) {
233+
trajectoryHeader.scrollIntoView({
234+
behavior: 'smooth',
235+
block: 'nearest'
236+
});
237+
}
238+
}, 100);
239+
}
240+
}
241+
221242

222243

223244
// Initialize everything when DOM is loaded

codeclash/viewer/templates/index.html

Lines changed: 146 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,153 @@ <h2>🎯 Game Rounds</h2>
7272
{% if round_num in trajectories_by_round %}
7373
{% for trajectory in trajectories_by_round[round_num] %}
7474

75-
<!-- Trajectory View -->
76-
<details class="foldout trajectory-foldout">
77-
<summary>🤖 Player {{ trajectory.player_id }} Round {{ round_num }} - Trajectory</summary>
78-
<div class="trajectory-content">
79-
{% include 'trajectory.html' %}
75+
<!-- Trajectory Container with Overview and Messages -->
76+
<div class="trajectory-header">
77+
<h3>🤖 Player {{ trajectory.player_id }} Round {{ round_num }}</h3>
78+
<div class="trajectory-stats">
79+
<div class="stat-item">
80+
<span class="stat-label">API Calls:</span>
81+
<span class="stat-value">{{ trajectory.api_calls }}</span>
82+
</div>
83+
<div class="stat-item">
84+
<span class="stat-label">Cost:</span>
85+
<span class="stat-value">${{ "%.2f"|format(trajectory.cost) }}</span>
86+
</div>
87+
{% if trajectory.exit_status %}
88+
<div class="stat-item">
89+
<span class="stat-label">Exit Status:</span>
90+
<span class="stat-value status-{{ trajectory.exit_status.lower() }}">{{ trajectory.exit_status }}</span>
91+
</div>
92+
{% endif %}
8093
</div>
81-
</details>
94+
95+
<!-- Messages Foldout inside the same container -->
96+
<details class="trajectory-messages-foldout">
97+
<summary>💬 Messages ({{ trajectory.messages|length }} total)</summary>
98+
<div class="trajectory-content">
99+
<div class="messages-container">
100+
{% for message in trajectory.messages %}
101+
<div class="message-block {{ message.role }}">
102+
<span class="message-role-badge">{{ message.role.title() }} #{{ loop.index }}</span>
103+
<div class="message-content">
104+
{% if message.content is string %}
105+
{% set content_lines = message.content.split('\n') %}
106+
{% if content_lines|length <= 5 %}
107+
<!-- Show full content if 5 lines or less -->
108+
<div class="message-content-full">
109+
{% if message.role == 'assistant' and '```' in message.content %}
110+
<!-- Special handling for assistant messages with code blocks -->
111+
{% set parts = message.content.split('```') %}
112+
{% for part in parts %}
113+
{% if loop.index is odd %}
114+
<!-- Text part -->
115+
<div class="message-text"><pre>{{ part }}</pre></div>
116+
{% else %}
117+
<!-- Code part -->
118+
<div class="code-block">
119+
<pre><code>{{ part }}</code></pre>
120+
</div>
121+
{% endif %}
122+
{% endfor %}
123+
{% else %}
124+
<!-- Regular text content -->
125+
<div class="message-text">
126+
<pre>{{ message.content }}</pre>
127+
</div>
128+
{% endif %}
129+
</div>
130+
{% else %}
131+
<!-- Show preview with expand button -->
132+
<div class="message-preview-short clickable-message" onclick="expandMessage(this)" title="Click to expand ({{ content_lines|length - 5 }} more lines)">
133+
<div class="message-text">
134+
<pre>{{ content_lines[:5] | join('\n') }}</pre>
135+
</div>
136+
<div class="expand-indicator">
137+
▼ {{ content_lines|length - 5 }} more lines
138+
</div>
139+
</div>
140+
<div class="message-content-full" style="display: none;" title="Content expanded - click collapse button below to hide">
141+
{% if message.role == 'assistant' and '```' in message.content %}
142+
<!-- Special handling for assistant messages with code blocks -->
143+
{% set parts = message.content.split('```') %}
144+
{% for part in parts %}
145+
{% if loop.index is odd %}
146+
<!-- Text part -->
147+
<div class="message-text"><pre>{{ part }}</pre></div>
148+
{% else %}
149+
<!-- Code part -->
150+
<div class="code-block">
151+
<pre><code>{{ part }}</code></pre>
152+
</div>
153+
{% endif %}
154+
{% endfor %}
155+
{% else %}
156+
<!-- Regular text content -->
157+
<div class="message-text">
158+
<pre>{{ message.content }}</pre>
159+
</div>
160+
{% endif %}
161+
<div class="collapse-indicator clickable-message" onclick="collapseMessage(this)" title="Click to collapse">
162+
▲ Click to collapse
163+
</div>
164+
</div>
165+
{% endif %}
166+
{% elif message.content is iterable %}
167+
<!-- Handle complex content (e.g., user messages with multiple parts) -->
168+
<div class="message-content-full">
169+
{% for content_part in message.content %}
170+
{% if content_part.type == 'text' %}
171+
{% set content_lines = content_part.text.split('\n') %}
172+
{% if content_lines|length <= 5 %}
173+
<div class="message-text">
174+
<pre>{{ content_part.text }}</pre>
175+
</div>
176+
{% else %}
177+
<div class="message-preview-short clickable-message" onclick="expandMessage(this)" title="Click to expand ({{ content_lines|length - 5 }} more lines)">
178+
<div class="message-text">
179+
<pre>{{ content_lines[:5] | join('\n') }}</pre>
180+
</div>
181+
<div class="expand-indicator">
182+
▼ {{ content_lines|length - 5 }} more lines
183+
</div>
184+
</div>
185+
<div class="message-content-expanded" style="display: none;" title="Content expanded - click collapse button below to hide">
186+
<div class="message-text">
187+
<pre>{{ content_part.text }}</pre>
188+
</div>
189+
<div class="collapse-indicator clickable-message" onclick="collapseMessage(this)" title="Click to collapse">
190+
▲ Click to collapse
191+
</div>
192+
</div>
193+
{% endif %}
194+
{% else %}
195+
<div class="message-part">
196+
<strong>{{ content_part.type.title() }}:</strong>
197+
<pre>{{ content_part | tojson(indent=2) }}</pre>
198+
</div>
199+
{% endif %}
200+
{% endfor %}
201+
</div>
202+
{% else %}
203+
<!-- Fallback for other content types -->
204+
<div class="message-content-full">
205+
<div class="message-text">
206+
<pre>{{ message.content | tojson(indent=2) }}</pre>
207+
</div>
208+
</div>
209+
{% endif %}
210+
</div>
211+
</div>
212+
{% endfor %}
213+
</div>
214+
<div class="collapse-messages-button">
215+
<button onclick="collapseTrajectoryMessages(this)" class="btn-collapse-messages">
216+
▲ Collapse Messages
217+
</button>
218+
</div>
219+
</div>
220+
</details>
221+
</div>
82222

83223
<!-- Submission -->
84224
{% if trajectory.submission %}

codeclash/viewer/templates/trajectory.html

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
<!-- Trajectory Template - displays individual agent trajectory data -->
2-
<div class="trajectory-info">
1+
<!-- Trajectory Overview - always visible -->
2+
<div class="trajectory-overview">
33
<div class="trajectory-stats">
44
<div class="stat-item">
55
<span class="stat-label">API Calls:</span>
66
<span class="stat-value">{{ trajectory.api_calls }}</span>
77
</div>
88
<div class="stat-item">
99
<span class="stat-label">Cost:</span>
10-
<span class="stat-value">${{ "%.6f"|format(trajectory.cost) }}</span>
10+
<span class="stat-value">${{ "%.2f"|format(trajectory.cost) }}</span>
1111
</div>
1212
{% if trajectory.exit_status %}
1313
<div class="stat-item">
@@ -18,6 +18,7 @@
1818
</div>
1919
</div>
2020

21+
<!-- Messages Container - for foldout content -->
2122
<div class="messages-container">
2223
{% for message in trajectory.messages %}
2324
<div class="message-block {{ message.role }}">

0 commit comments

Comments
 (0)