assafvayner HF Staff commited on
Commit
b60649b
·
1 Parent(s): 9e075cf
Files changed (2) hide show
  1. src/App.svelte +139 -30
  2. src/app.css +14 -6
src/App.svelte CHANGED
@@ -143,31 +143,128 @@
143
  return parts;
144
  }
145
 
146
- // Create collapsible backtrace structure
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
  function createCollapsibleBacktrace(backtrace) {
148
- return backtrace.map((line, index) => ({
149
- id: index,
150
- line,
151
- parsed: parseBacktraceLine(line),
152
- collapsed: false,
153
- }));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  }
155
 
156
- // Toggle backtrace line collapse
157
- function toggleBacktraceLine(threadId, lineIndex) {
 
 
 
 
 
 
158
  threads = threads.map((thread) => {
159
  if (thread.id === threadId && thread.collapsibleBacktrace) {
160
  const updatedBacktrace = [...thread.collapsibleBacktrace];
161
- updatedBacktrace[lineIndex] = {
162
- ...updatedBacktrace[lineIndex],
163
- collapsed: !updatedBacktrace[lineIndex].collapsed,
164
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  return { ...thread, collapsibleBacktrace: updatedBacktrace };
166
  }
167
  return thread;
168
  });
169
  }
170
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  // Reactive updates - run filter when threads OR filterText changes
172
  $: filteredThreads = threads.filter(
173
  (thread) =>
@@ -230,24 +327,36 @@
230
  <div class="thread-content">
231
  <div class="backtrace">
232
  {#each thread.collapsibleBacktrace as backtraceLine (backtraceLine.id)}
233
- <div
234
- class="backtrace-line"
235
- class:collapsed={backtraceLine.collapsed}
236
- on:click={() =>
237
- toggleBacktraceLine(thread.id, backtraceLine.id)}
238
- on:keydown={(e) =>
239
- e.key === "Enter" &&
240
- toggleBacktraceLine(thread.id, backtraceLine.id)}
241
- role="button"
242
- tabindex="0"
243
- >
244
- <span class="indent">{backtraceLine.parsed.indent}</span
245
- >{#if backtraceLine.parsed.sampleCount}<span
246
- class="sample-count"
247
- >{backtraceLine.parsed.sampleCount}</span
 
 
 
 
 
 
 
 
 
 
 
 
248
  >
249
- {/if}<span class="content">{backtraceLine.parsed.content}</span>
250
- </div>
251
  {/each}
252
  </div>
253
  </div>
 
143
  return parts;
144
  }
145
 
146
+ // Parse indentation level from a backtrace line
147
+ function getIndentationLevel(line) {
148
+ const match = line.match(/^(\s*[\+\!\:\|]*\s*)/);
149
+ return match ? match[1].length : 0;
150
+ }
151
+
152
+ // Find the end of a section (lines with greater indentation)
153
+ function findSectionEnd(backtrace, startIndex) {
154
+ const startIndentation = getIndentationLevel(backtrace[startIndex]);
155
+
156
+ for (let i = startIndex + 1; i < backtrace.length; i++) {
157
+ const currentIndentation = getIndentationLevel(backtrace[i]);
158
+ // If we find a line with same or lesser indentation, that's where the section ends
159
+ if (currentIndentation <= startIndentation) {
160
+ return i - 1; // Return the last line of the section
161
+ }
162
+ }
163
+ // If we reach the end, the section goes to the last line
164
+ return backtrace.length - 1;
165
+ }
166
+
167
+ // Create collapsible backtrace structure with section awareness
168
  function createCollapsibleBacktrace(backtrace) {
169
+ const result = backtrace.map((line, index) => {
170
+ const indentationLevel = getIndentationLevel(line);
171
+ const sectionEnd = findSectionEnd(backtrace, index);
172
+ const hasSection = sectionEnd > index; // This line has child lines with greater indentation
173
+
174
+ return {
175
+ id: index,
176
+ line,
177
+ parsed: parseBacktraceLine(line),
178
+ indentationLevel,
179
+ hasSection,
180
+ sectionEnd,
181
+ sectionCollapsed: false,
182
+ hidden: false, // Will be set based on parent section state
183
+ };
184
+ });
185
+
186
+ // Initialize visibility based on any collapsed sections
187
+ result.forEach((_, index) => {
188
+ result[index].hidden = isLineHiddenByParentSection(result, index);
189
+ });
190
+
191
+ return result;
192
  }
193
 
194
+ // Toggle section collapse for a specific line
195
+ function toggleSection(threadId, lineIndex) {
196
+ console.log(
197
+ "🔄 Toggling section for thread:",
198
+ threadId,
199
+ "line:",
200
+ lineIndex
201
+ );
202
  threads = threads.map((thread) => {
203
  if (thread.id === threadId && thread.collapsibleBacktrace) {
204
  const updatedBacktrace = [...thread.collapsibleBacktrace];
205
+ const line = updatedBacktrace[lineIndex];
206
+
207
+ if (line.hasSection) {
208
+ // Toggle the section collapsed state
209
+ line.sectionCollapsed = !line.sectionCollapsed;
210
+ console.log(
211
+ "📊 Section",
212
+ lineIndex,
213
+ "collapsed:",
214
+ line.sectionCollapsed,
215
+ "affects lines",
216
+ lineIndex + 1,
217
+ "to",
218
+ line.sectionEnd
219
+ );
220
+
221
+ // Update hidden state for all lines in this section
222
+ updateSectionVisibility(updatedBacktrace, lineIndex);
223
+ } else {
224
+ console.log("⚠️ Line", lineIndex, "has no section to toggle");
225
+ }
226
+
227
  return { ...thread, collapsibleBacktrace: updatedBacktrace };
228
  }
229
  return thread;
230
  });
231
  }
232
 
233
+ // Update visibility of lines based on section collapse states
234
+ function updateSectionVisibility(backtrace, changedLineIndex) {
235
+ const changedLine = backtrace[changedLineIndex];
236
+
237
+ // Update visibility for lines in the changed section
238
+ for (let i = changedLineIndex + 1; i <= changedLine.sectionEnd; i++) {
239
+ if (changedLine.sectionCollapsed) {
240
+ backtrace[i].hidden = true;
241
+ } else {
242
+ // Only show if not hidden by a parent section
243
+ backtrace[i].hidden = isLineHiddenByParentSection(backtrace, i);
244
+ }
245
+ }
246
+ }
247
+
248
+ // Check if a line should be hidden by any parent section
249
+ function isLineHiddenByParentSection(backtrace, lineIndex) {
250
+ const currentIndentation = backtrace[lineIndex].indentationLevel;
251
+
252
+ // Look backwards for any parent sections that are collapsed
253
+ for (let i = lineIndex - 1; i >= 0; i--) {
254
+ const parentLine = backtrace[i];
255
+ if (
256
+ parentLine.indentationLevel < currentIndentation &&
257
+ parentLine.hasSection &&
258
+ parentLine.sectionCollapsed &&
259
+ i + 1 <= lineIndex &&
260
+ lineIndex <= parentLine.sectionEnd
261
+ ) {
262
+ return true;
263
+ }
264
+ }
265
+ return false;
266
+ }
267
+
268
  // Reactive updates - run filter when threads OR filterText changes
269
  $: filteredThreads = threads.filter(
270
  (thread) =>
 
327
  <div class="thread-content">
328
  <div class="backtrace">
329
  {#each thread.collapsibleBacktrace as backtraceLine (backtraceLine.id)}
330
+ {#if !backtraceLine.hidden}
331
+ <div
332
+ class="backtrace-line"
333
+ class:has-section={backtraceLine.hasSection}
334
+ class:section-collapsed={backtraceLine.sectionCollapsed}
335
+ on:click={backtraceLine.hasSection
336
+ ? () => toggleSection(thread.id, backtraceLine.id)
337
+ : undefined}
338
+ on:keydown={backtraceLine.hasSection
339
+ ? (e) =>
340
+ e.key === "Enter" &&
341
+ toggleSection(thread.id, backtraceLine.id)
342
+ : undefined}
343
+ role={backtraceLine.hasSection ? "button" : undefined}
344
+ tabindex={backtraceLine.hasSection ? "0" : undefined}
345
+ >
346
+ <span class="indent">{backtraceLine.parsed.indent}</span
347
+ >{#if backtraceLine.hasSection}
348
+ <span class="collapse-indicator">
349
+ {backtraceLine.sectionCollapsed ? "▶" : "▼"}
350
+ </span>
351
+ {/if}{#if backtraceLine.parsed.sampleCount}<span
352
+ class="sample-count"
353
+ >{backtraceLine.parsed.sampleCount}</span
354
+ >
355
+ {/if}<span class="content"
356
+ >{backtraceLine.parsed.content}</span
357
  >
358
+ </div>
359
+ {/if}
360
  {/each}
361
  </div>
362
  </div>
src/app.css CHANGED
@@ -127,20 +127,28 @@ body {
127
 
128
  .backtrace-line {
129
  margin: 2px 0;
130
- cursor: pointer;
131
  padding: 1px 0;
 
 
 
 
 
132
  }
133
 
134
- .backtrace-line:hover {
135
  background-color: #f0f8ff;
136
  }
137
 
138
- .backtrace-line.collapsed {
139
- color: #666;
 
 
 
 
140
  }
141
 
142
- .backtrace-line.collapsed .content {
143
- display: none;
144
  }
145
 
146
  .backtrace-line .indent {
 
127
 
128
  .backtrace-line {
129
  margin: 2px 0;
 
130
  padding: 1px 0;
131
+ cursor: default;
132
+ }
133
+
134
+ .backtrace-line.has-section {
135
+ cursor: pointer;
136
  }
137
 
138
+ .backtrace-line.has-section:hover {
139
  background-color: #f0f8ff;
140
  }
141
 
142
+ .collapse-indicator {
143
+ color: #3498db;
144
+ font-size: 10px;
145
+ margin-right: 4px;
146
+ font-weight: bold;
147
+ user-select: none;
148
  }
149
 
150
+ .backtrace-line.section-collapsed .collapse-indicator {
151
+ color: #95a5a6;
152
  }
153
 
154
  .backtrace-line .indent {