Skip to content

Commit 19b9099

Browse files
committed
Enh(viewer): Only click body to expand (allows easier highlight+copy)
1 parent f140b28 commit 19b9099

2 files changed

Lines changed: 39 additions & 131 deletions

File tree

codeclash/viewer/static/js/app.js

Lines changed: 33 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -111,103 +111,7 @@ function initializeKeyboardShortcuts() {
111111
});
112112
}
113113

114-
// Search functionality (basic)
115-
function initializeSearch() {
116-
// Add search input to header if it doesn't exist
117-
const controls = document.querySelector('.controls');
118-
if (controls && !document.getElementById('search-input')) {
119-
const searchGroup = document.createElement('div');
120-
searchGroup.className = 'control-group';
121-
122-
const searchLabel = document.createElement('label');
123-
searchLabel.setAttribute('for', 'search-input');
124-
searchLabel.textContent = 'Search:';
125-
126-
const searchInput = document.createElement('input');
127-
searchInput.type = 'text';
128-
searchInput.id = 'search-input';
129-
searchInput.placeholder = 'Search messages...';
130-
searchInput.style.padding = '0.5rem';
131-
searchInput.style.border = '1px solid var(--border-color)';
132-
searchInput.style.borderRadius = '0.375rem';
133-
searchInput.style.backgroundColor = 'var(--bg-primary)';
134-
searchInput.style.color = 'var(--text-primary)';
135-
searchInput.style.fontSize = '0.875rem';
136-
searchInput.style.minWidth = '200px';
137-
138-
searchGroup.appendChild(searchLabel);
139-
searchGroup.appendChild(searchInput);
140-
controls.insertBefore(searchGroup, controls.lastElementChild);
141-
142-
// Add search functionality
143-
let searchTimeout;
144-
searchInput.addEventListener('input', function(e) {
145-
clearTimeout(searchTimeout);
146-
searchTimeout = setTimeout(() => {
147-
performSearch(e.target.value);
148-
}, 300);
149-
});
150-
}
151-
}
152-
153-
function performSearch(searchTerm) {
154-
const messages = document.querySelectorAll('.message-content');
155-
const highlights = document.querySelectorAll('.search-highlight');
156-
157-
// Clear previous highlights
158-
highlights.forEach(highlight => {
159-
const parent = highlight.parentNode;
160-
parent.replaceChild(document.createTextNode(highlight.textContent), highlight);
161-
parent.normalize();
162-
});
163-
164-
if (!searchTerm.trim()) {
165-
return;
166-
}
167-
168-
const regex = new RegExp(`(${escapeRegExp(searchTerm)})`, 'gi');
169-
170-
messages.forEach(messageContent => {
171-
const textNodes = getTextNodes(messageContent);
172-
textNodes.forEach(node => {
173-
if (regex.test(node.textContent)) {
174-
const parent = node.parentNode;
175-
const highlighted = node.textContent.replace(regex, '<mark class="search-highlight">$1</mark>');
176-
const wrapper = document.createElement('span');
177-
wrapper.innerHTML = highlighted;
178-
parent.replaceChild(wrapper, node);
179-
180-
// Open the containing details if closed
181-
let detailsParent = parent.closest('details');
182-
while (detailsParent) {
183-
detailsParent.setAttribute('open', '');
184-
detailsParent = detailsParent.parentElement.closest('details');
185-
}
186-
}
187-
});
188-
});
189-
}
190-
191-
function getTextNodes(element) {
192-
const textNodes = [];
193-
const walker = document.createTreeWalker(
194-
element,
195-
NodeFilter.SHOW_TEXT,
196-
null,
197-
false
198-
);
199-
200-
let node;
201-
while (node = walker.nextNode()) {
202-
textNodes.push(node);
203-
}
204-
205-
return textNodes;
206-
}
207114

208-
function escapeRegExp(string) {
209-
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
210-
}
211115

212116
// Code highlighting (basic syntax highlighting)
213117
function initializeCodeHighlighting() {
@@ -274,39 +178,44 @@ function initializePerformanceMonitoring() {
274178
}
275179

276180
// Message expand/collapse functionality
277-
function toggleMessageExpand(clickedElement) {
181+
function expandMessage(clickedElement) {
278182
const messageContent = clickedElement.closest('.message-content');
279183
const previewShort = messageContent.querySelector('.message-preview-short');
280184
const contentFull = messageContent.querySelector('.message-content-full');
281185
const contentExpanded = messageContent.querySelector('.message-content-expanded');
282186

283-
if (clickedElement.classList.contains('message-preview-short')) {
284-
// Expanding - hide preview, show full content
285-
if (previewShort) previewShort.style.display = 'none';
286-
if (contentFull) contentFull.style.display = 'block';
287-
if (contentExpanded) contentExpanded.style.display = 'block';
288-
289-
// Smooth scroll to keep the content in view
290-
setTimeout(() => {
291-
messageContent.scrollIntoView({
292-
behavior: 'smooth',
293-
block: 'nearest'
294-
});
295-
}, 100);
296-
} else if (clickedElement.classList.contains('message-content-full') || clickedElement.classList.contains('message-content-expanded')) {
297-
// Collapsing - show preview, hide full content
298-
if (contentFull) contentFull.style.display = 'none';
299-
if (contentExpanded) contentExpanded.style.display = 'none';
300-
if (previewShort) previewShort.style.display = 'block';
301-
302-
// Smooth scroll to keep the content in view
303-
setTimeout(() => {
304-
messageContent.scrollIntoView({
305-
behavior: 'smooth',
306-
block: 'nearest'
307-
});
308-
}, 100);
309-
}
187+
// Expanding - hide preview, show full content
188+
if (previewShort) previewShort.style.display = 'none';
189+
if (contentFull) contentFull.style.display = 'block';
190+
if (contentExpanded) contentExpanded.style.display = 'block';
191+
192+
// Smooth scroll to keep the content in view
193+
setTimeout(() => {
194+
messageContent.scrollIntoView({
195+
behavior: 'smooth',
196+
block: 'nearest'
197+
});
198+
}, 100);
199+
}
200+
201+
function collapseMessage(clickedElement) {
202+
const messageContent = clickedElement.closest('.message-content');
203+
const previewShort = messageContent.querySelector('.message-preview-short');
204+
const contentFull = messageContent.querySelector('.message-content-full');
205+
const contentExpanded = messageContent.querySelector('.message-content-expanded');
206+
207+
// Collapsing - show preview, hide full content
208+
if (contentFull) contentFull.style.display = 'none';
209+
if (contentExpanded) contentExpanded.style.display = 'none';
210+
if (previewShort) previewShort.style.display = 'block';
211+
212+
// Smooth scroll to keep the content in view
213+
setTimeout(() => {
214+
messageContent.scrollIntoView({
215+
behavior: 'smooth',
216+
block: 'nearest'
217+
});
218+
}, 100);
310219
}
311220

312221

@@ -316,7 +225,6 @@ document.addEventListener('DOMContentLoaded', function() {
316225
initializeTheme();
317226
initializeFoldouts();
318227
initializeKeyboardShortcuts();
319-
initializeSearch();
320228
initializeCodeHighlighting();
321229
initializePerformanceMonitoring();
322230

codeclash/viewer/templates/trajectory.html

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,15 @@
5151
</div>
5252
{% else %}
5353
<!-- Show preview with expand button -->
54-
<div class="message-preview-short clickable-message" onclick="toggleMessageExpand(this)" title="Click to expand ({{ content_lines|length - 5 }} more lines)">
54+
<div class="message-preview-short clickable-message" onclick="expandMessage(this)" title="Click to expand ({{ content_lines|length - 5 }} more lines)">
5555
<div class="message-text">
5656
<pre>{{ content_lines[:5] | join('\n') }}</pre>
5757
</div>
5858
<div class="expand-indicator">
5959
▼ {{ content_lines|length - 5 }} more lines
6060
</div>
6161
</div>
62-
<div class="message-content-full clickable-message" style="display: none;" onclick="toggleMessageExpand(this)" title="Click to collapse">
62+
<div class="message-content-full" style="display: none;" title="Content expanded - click collapse button below to hide">
6363
{% if message.role == 'assistant' and '```' in message.content %}
6464
<!-- Special handling for assistant messages with code blocks -->
6565
{% set parts = message.content.split('```') %}
@@ -80,7 +80,7 @@
8080
<pre>{{ message.content }}</pre>
8181
</div>
8282
{% endif %}
83-
<div class="collapse-indicator">
83+
<div class="collapse-indicator clickable-message" onclick="collapseMessage(this)" title="Click to collapse">
8484
▲ Click to collapse
8585
</div>
8686
</div>
@@ -96,19 +96,19 @@
9696
<pre>{{ content_part.text }}</pre>
9797
</div>
9898
{% else %}
99-
<div class="message-preview-short clickable-message" onclick="toggleMessageExpand(this)" title="Click to expand ({{ content_lines|length - 5 }} more lines)">
99+
<div class="message-preview-short clickable-message" onclick="expandMessage(this)" title="Click to expand ({{ content_lines|length - 5 }} more lines)">
100100
<div class="message-text">
101101
<pre>{{ content_lines[:5] | join('\n') }}</pre>
102102
</div>
103103
<div class="expand-indicator">
104104
▼ {{ content_lines|length - 5 }} more lines
105105
</div>
106106
</div>
107-
<div class="message-content-expanded clickable-message" style="display: none;" onclick="toggleMessageExpand(this)" title="Click to collapse">
107+
<div class="message-content-expanded" style="display: none;" title="Content expanded - click collapse button below to hide">
108108
<div class="message-text">
109109
<pre>{{ content_part.text }}</pre>
110110
</div>
111-
<div class="collapse-indicator">
111+
<div class="collapse-indicator clickable-message" onclick="collapseMessage(this)" title="Click to collapse">
112112
▲ Click to collapse
113113
</div>
114114
</div>

0 commit comments

Comments
 (0)