Skip to content

Commit bb1fdeb

Browse files
committed
Enh(viewer): Folder collapsed by default
1 parent 822607d commit bb1fdeb

3 files changed

Lines changed: 139 additions & 55 deletions

File tree

codeclash/viewer/static/css/picker.css

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,28 +23,6 @@
2323
font-size: 1.1rem;
2424
}
2525

26-
.base-dir-info {
27-
background-color: var(--bg-secondary);
28-
border: 1px solid var(--border-color);
29-
border-radius: 0.5rem;
30-
padding: 1rem;
31-
margin-bottom: 2rem;
32-
}
33-
34-
.base-dir-info h3 {
35-
margin: 0 0 0.5rem 0;
36-
color: var(--text-primary);
37-
}
38-
39-
.base-dir-path {
40-
background-color: var(--code-bg);
41-
padding: 0.5rem;
42-
border-radius: 0.25rem;
43-
font-family: monospace;
44-
font-size: 0.9rem;
45-
word-break: break-all;
46-
}
47-
4826
.games-table {
4927
background-color: var(--bg-secondary);
5028
border: 1px solid var(--border-color);
@@ -244,6 +222,36 @@
244222
flex-shrink: 0;
245223
}
246224

225+
.collapse-icon {
226+
color: var(--accent-color);
227+
font-size: 0.8rem;
228+
font-weight: bold;
229+
cursor: pointer;
230+
transition: transform 0.2s ease;
231+
user-select: none;
232+
min-width: 12px;
233+
text-align: center;
234+
}
235+
236+
/* Only show collapse icon for intermediate folders */
237+
.intermediate-folder .collapse-icon::before {
238+
content: "▶";
239+
}
240+
241+
.intermediate-folder:not(.collapsed) .collapse-icon::before {
242+
content: "▼";
243+
}
244+
245+
/* Hide collapse icon for game folders */
246+
.game-folder .collapse-icon {
247+
display: none;
248+
}
249+
250+
.collapse-icon:hover {
251+
color: var(--accent-hover);
252+
transform: scale(1.1);
253+
}
254+
247255
.game-name {
248256
font-family: monospace;
249257
font-size: 0.9rem;

codeclash/viewer/static/js/picker.js

Lines changed: 106 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -135,48 +135,109 @@ function toggleFolder(folderPath) {
135135
const folderRow = document.querySelector(`[data-path="${folderPath}"]`);
136136
if (!folderRow) return;
137137

138-
const isCollapsed = folderRow.classList.contains("collapsed");
139-
const collapseIcon = folderRow.querySelector(".collapse-icon");
138+
const currentState = folderStates.get(folderPath) || "collapsed";
139+
const newState = currentState === "collapsed" ? "expanded" : "collapsed";
140+
folderStates.set(folderPath, newState);
140141

141-
if (isCollapsed) {
142-
// Expand folder - show all children
142+
if (newState === "expanded") {
143+
// Expand folder - show only direct children
143144
folderRow.classList.remove("collapsed");
144-
if (collapseIcon) collapseIcon.innerHTML = '<i class="bi bi-folder"></i>';
145145

146-
// Show all descendant rows
146+
// Show only direct children (one level down) and restore their states
147147
const allRows = document.querySelectorAll(".game-row");
148148
allRows.forEach((row) => {
149149
const rowPath = row.getAttribute("data-path");
150150
if (rowPath && rowPath.startsWith(folderPath + "/")) {
151-
row.style.display = "";
152-
// If this child row is also a collapsed folder, don't show its children
153-
const childFolderPath = rowPath;
154-
const childRow = document.querySelector(
155-
`[data-path="${childFolderPath}"]`,
156-
);
157-
if (childRow && childRow.classList.contains("collapsed")) {
158-
// Hide this collapsed folder's children
159-
hideChildrenOfFolder(childFolderPath);
151+
// Check if this is a direct child (not a grandchild)
152+
const relativePath = rowPath.substring(folderPath.length + 1);
153+
if (!relativePath.includes("/")) {
154+
// This is a direct child, show it
155+
row.style.display = "";
156+
157+
// If this is a folder, restore its individual state
158+
if (row.classList.contains("intermediate-folder")) {
159+
const childState = folderStates.get(rowPath) || "collapsed";
160+
if (childState === "expanded") {
161+
// This child was expanded, so expand it and show its children
162+
row.classList.remove("collapsed");
163+
showChildrenOfFolder(rowPath);
164+
} else {
165+
// This child was collapsed, so collapse it and hide its children
166+
row.classList.add("collapsed");
167+
hideChildrenOfFolder(rowPath);
168+
}
169+
}
160170
}
161171
}
162172
});
163173
} else {
164174
// Collapse folder - hide all children
165175
folderRow.classList.add("collapsed");
166-
if (collapseIcon)
167-
collapseIcon.innerHTML = '<i class="bi bi-folder-open"></i>';
168176

169177
hideChildrenOfFolder(folderPath);
170178
}
171179
}
172180

173181
function hideChildrenOfFolder(folderPath) {
174-
// Hide all descendant rows of a folder
182+
// Hide all descendant rows of a folder, but respect their individual states
183+
const allRows = document.querySelectorAll(".game-row");
184+
allRows.forEach((row) => {
185+
const rowPath = row.getAttribute("data-path");
186+
if (rowPath && rowPath.startsWith(folderPath + "/")) {
187+
// Check if this is a direct child or a descendant
188+
const relativePath = rowPath.substring(folderPath.length + 1);
189+
190+
if (!relativePath.includes("/")) {
191+
// This is a direct child - hide it
192+
row.style.display = "none";
193+
} else {
194+
// This is a grandchild or deeper - check if its parent is visible
195+
const parentPath = rowPath.substring(0, rowPath.lastIndexOf("/"));
196+
const parentRow = document.querySelector(`[data-path="${parentPath}"]`);
197+
198+
if (parentRow && parentRow.style.display === "none") {
199+
// Parent is hidden, so hide this child too
200+
row.style.display = "none";
201+
}
202+
// If parent is visible, don't change this child's visibility
203+
// (it will be handled by its own parent's state)
204+
}
205+
}
206+
});
207+
}
208+
209+
function showChildrenOfFolder(folderPath) {
210+
// Show all descendant rows of a folder, respecting their individual states
175211
const allRows = document.querySelectorAll(".game-row");
176212
allRows.forEach((row) => {
177213
const rowPath = row.getAttribute("data-path");
178214
if (rowPath && rowPath.startsWith(folderPath + "/")) {
179-
row.style.display = "none";
215+
// Check if this is a direct child or a descendant
216+
const relativePath = rowPath.substring(folderPath.length + 1);
217+
218+
if (!relativePath.includes("/")) {
219+
// This is a direct child - show it
220+
row.style.display = "";
221+
} else {
222+
// This is a grandchild or deeper - check if its parent is visible and expanded
223+
const parentPath = rowPath.substring(0, rowPath.lastIndexOf("/"));
224+
const parentRow = document.querySelector(`[data-path="${parentPath}"]`);
225+
226+
if (parentRow && parentRow.style.display !== "none") {
227+
// Parent is visible, check if it's expanded
228+
const parentState = folderStates.get(parentPath) || "collapsed";
229+
if (parentState === "expanded") {
230+
// Parent is expanded, so show this child
231+
row.style.display = "";
232+
} else {
233+
// Parent is collapsed, so hide this child
234+
row.style.display = "none";
235+
}
236+
} else {
237+
// Parent is hidden, so hide this child too
238+
row.style.display = "none";
239+
}
240+
}
180241
}
181242
});
182243
}
@@ -536,4 +597,30 @@ document.addEventListener("DOMContentLoaded", function () {
536597
console.log("Available keyboard shortcuts:");
537598
console.log(" Shift + Click: Range select checkboxes");
538599
console.log(" Escape: Close move dialog");
600+
601+
// Collapse all folders by default
602+
collapseAllFolders();
539603
});
604+
605+
// Track individual folder states
606+
const folderStates = new Map();
607+
608+
function collapseAllFolders() {
609+
// Find all intermediate folder rows and collapse them
610+
const folderRows = document.querySelectorAll(".intermediate-folder");
611+
folderRows.forEach((folderRow) => {
612+
const folderPath = folderRow.getAttribute("data-path");
613+
if (folderPath) {
614+
// Set initial state to collapsed
615+
folderStates.set(folderPath, "collapsed");
616+
617+
// Collapse the folder
618+
folderRow.classList.add("collapsed");
619+
620+
// Hide all children of this folder
621+
hideChildrenOfFolder(folderPath);
622+
}
623+
});
624+
625+
console.log(`Collapsed ${folderRows.length} folders on startup`);
626+
}

codeclash/viewer/templates/picker.html

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ <h1><i class="bi bi-controller"></i> Pick a Game</h1>
2020
<p>Select a game session to view its trajectory</p>
2121
</div>
2222

23-
<div class="base-dir-info">
24-
<h3><i class="bi bi-folder"></i> Base Directory:</h3>
25-
<div class="base-dir-path">{{ base_dir }}</div>
26-
</div>
2723

2824
{% if game_folders %}
2925
<div class="games-table">
@@ -55,8 +51,8 @@ <h3><i class="bi bi-folder"></i> Base Directory:</h3>
5551
{% endif %}
5652
</div>
5753
<div class="table-header">
58-
<div><i class="bi bi-check-square"></i> Select</div>
59-
<div><i class="bi bi-folder"></i> Game Session</div>
54+
<div><i class="bi bi-check-square"></i></div>
55+
<div>Game Session</div>
6056
<div><i class="bi bi-journal-text"></i> Note</div>
6157
<div><i class="bi bi-target"></i> Rounds</div>
6258
<div><i class="bi bi-robot"></i> Models</div>
@@ -84,15 +80,8 @@ <h3><i class="bi bi-folder"></i> Base Directory:</h3>
8480
{% for i in range(depth) %}
8581
<span class="folder-icon">&nbsp;&nbsp;</span>
8682
{% endfor %}
87-
{% if depth > 0 %}
88-
<span class="folder-icon">└─</span>
89-
{% endif %}
9083
</span>
91-
{% if game.is_game %}
92-
<span class="folder-icon"><i class="bi bi-controller"></i></span>
93-
{% else %}
94-
<span class="folder-icon collapse-icon"><i class="bi bi-folder"></i></span>
95-
{% endif %}
84+
<span class="folder-icon collapse-icon"></span>
9685
<span class="game-name">{{ folder_name }}</span>
9786
</div>
9887

0 commit comments

Comments
 (0)