claude-code-log 0.2.7__tar.gz → 0.2.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/.claude/settings.local.json +6 -1
  2. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/CHANGELOG.md +7 -0
  3. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/PKG-INFO +1 -1
  4. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/claude_code_log/templates/transcript.html +298 -2
  5. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/pyproject.toml +1 -1
  6. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/uv.lock +1 -1
  7. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/.github/workflows/ci.yml +0 -0
  8. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/.github/workflows/docs.yml +0 -0
  9. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/.gitignore +0 -0
  10. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/CLAUDE.md +0 -0
  11. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/LICENSE +0 -0
  12. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/README.md +0 -0
  13. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/claude_code_log/__init__.py +0 -0
  14. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/claude_code_log/cli.py +0 -0
  15. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/claude_code_log/converter.py +0 -0
  16. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/claude_code_log/models.py +0 -0
  17. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/claude_code_log/parser.py +0 -0
  18. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/claude_code_log/py.typed +0 -0
  19. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/claude_code_log/renderer.py +0 -0
  20. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/claude_code_log/templates/index.html +0 -0
  21. {claude_code_log-0.2.7 → claude_code_log-0.2.8}/justfile +0 -0
@@ -30,7 +30,12 @@
30
30
  "Bash(open /tmp/test_preview.html)",
31
31
  "Bash(open /tmp/test_improved_preview.html)",
32
32
  "Bash(open /tmp/test_fixed_preview.html)",
33
- "Bash(git tag:*)"
33
+ "Bash(git tag:*)",
34
+ "Bash(open /tmp/test_filter.html)",
35
+ "Bash(open /tmp/test_toolbar.html)",
36
+ "Bash(open /tmp/test_sticky.html)",
37
+ "Bash(open /tmp/test_counts.html)",
38
+ "Bash(open /tmp/test_grid.html)"
34
39
  ],
35
40
  "deny": []
36
41
  }
@@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
 
9
+ ## [0.2.8] - 2025-06-23
10
+
11
+ ### Changed
12
+
13
+ - **Implement filtering by message type in transcripts**
14
+
15
+
9
16
  ## [0.2.7] - 2025-06-21
10
17
 
11
18
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-code-log
3
- Version: 0.2.7
3
+ Version: 0.2.8
4
4
  Summary: Convert Claude Code transcript JSONL files to HTML
5
5
  Project-URL: Homepage, https://github.com/daaain/claude-code-log
6
6
  Project-URL: Issues, https://github.com/daaain/claude-code-log/issues
@@ -312,6 +312,143 @@
312
312
  transform: translateY(-2px);
313
313
  }
314
314
 
315
+ .filter-toolbar {
316
+ background-color: #f8f9fa66;
317
+ border-radius: 8px;
318
+ padding: 12px 16px;
319
+ margin-bottom: 16px;
320
+ box-shadow: -7px -7px 10px #eeeeee44, 7px 7px 10px #00000011;
321
+ border-left: #ffffff66 1px solid;
322
+ border-top: #ffffff66 1px solid;
323
+ border-bottom: #00000017 1px solid;
324
+ border-right: #00000017 1px solid;
325
+ display: none;
326
+ position: sticky;
327
+ top: 0;
328
+ z-index: 100;
329
+ backdrop-filter: blur(8px);
330
+ }
331
+
332
+ .filter-toolbar.visible {
333
+ display: grid;
334
+ grid-template-columns: auto 1fr auto;
335
+ align-items: center;
336
+ gap: 16px;
337
+ }
338
+
339
+ .filter-label {
340
+ white-space: nowrap;
341
+ }
342
+
343
+ .filter-toggles {
344
+ display: flex;
345
+ flex-wrap: wrap;
346
+ align-items: center;
347
+ gap: 8px;
348
+ justify-content: center;
349
+ }
350
+
351
+ .filter-messages {
352
+ position: fixed;
353
+ bottom: 140px;
354
+ right: 20px;
355
+ background-color: #e8f4fd66;
356
+ color: #666;
357
+ border: none;
358
+ border-radius: 50%;
359
+ width: 50px;
360
+ height: 50px;
361
+ font-size: 1.2em;
362
+ line-height: 1;
363
+ cursor: pointer;
364
+ display: flex;
365
+ align-items: center;
366
+ justify-content: center;
367
+ box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2);
368
+ transition: background-color 0.3s, transform 0.2s;
369
+ z-index: 1000;
370
+ }
371
+
372
+ .filter-messages:hover {
373
+ background-color: #e8f4fdcc;
374
+ transform: translateY(-2px);
375
+ }
376
+
377
+ .filter-messages.active {
378
+ background-color: #fff3cd66;
379
+ }
380
+
381
+ .filter-toolbar h3 {
382
+ margin: 0;
383
+ font-size: 1em;
384
+ color: #495057;
385
+ font-weight: 600;
386
+ }
387
+
388
+ .filter-toggle {
389
+ padding: 6px 12px;
390
+ border: 1px solid #dee2e6;
391
+ border-radius: 20px;
392
+ background-color: transparent;
393
+ color: #495057;
394
+ font-size: 0.85em;
395
+ font-weight: 500;
396
+ cursor: pointer;
397
+ transition: all 0.2s;
398
+ display: flex;
399
+ align-items: center;
400
+ gap: 4px;
401
+ white-space: nowrap;
402
+ }
403
+
404
+ .filter-toggle:hover {
405
+ background-color: #ffffff99;
406
+ transform: translateY(-1px);
407
+ }
408
+
409
+ .filter-toggle.active {
410
+ background-color: #ffffffaa;
411
+ }
412
+
413
+ .filter-toggle.active:hover {
414
+ background-color: #ffffff66;
415
+ }
416
+
417
+ .filter-actions {
418
+ display: flex;
419
+ gap: 6px;
420
+ white-space: nowrap;
421
+ }
422
+
423
+ .filter-action-btn {
424
+ padding: 4px 8px;
425
+ border: 1px solid #dee2e6;
426
+ border-radius: 4px;
427
+ background-color: #ffffff66;
428
+ color: #6c757d;
429
+ font-size: 0.75em;
430
+ cursor: pointer;
431
+ transition: background-color 0.2s;
432
+ }
433
+
434
+ .filter-action-btn:hover {
435
+ background-color: #ffffff99;
436
+ }
437
+
438
+ .filter-toggle .count {
439
+ opacity: 0.7;
440
+ font-size: 0.9em;
441
+ margin-left: 2px;
442
+ }
443
+
444
+ .filter-toggle.active .count {
445
+ opacity: 1;
446
+ }
447
+
448
+ .message.filtered-hidden {
449
+ display: none;
450
+ }
451
+
315
452
  .session-summary {
316
453
  background-color: #ffffff66;
317
454
  border-left: #4caf5088 4px solid;
@@ -442,6 +579,31 @@
442
579
  <body>
443
580
  <h1 id="title">{{ title }}</h1>
444
581
 
582
+ <!-- Filter Toolbar -->
583
+ <div class="filter-toolbar">
584
+ <div class="filter-label">
585
+ <h3>Filter:</h3>
586
+ </div>
587
+ <div class="filter-toggles">
588
+ <button class="filter-toggle active" data-type="user">🤷 User <span class="count">(0)</span></button>
589
+ <button class="filter-toggle active" data-type="assistant">🤖 Assistant <span
590
+ class="count">(0)</span></button>
591
+ <button class="filter-toggle active" data-type="system">⚙️ System <span class="count">(0)</span></button>
592
+ <button class="filter-toggle active" data-type="tool_use">🛠️ Tool Use <span
593
+ class="count">(0)</span></button>
594
+ <button class="filter-toggle active" data-type="tool_result">🧰 Tool Results <span
595
+ class="count">(0)</span></button>
596
+ <button class="filter-toggle active" data-type="thinking">💭 Thinking <span
597
+ class="count">(0)</span></button>
598
+ <button class="filter-toggle active" data-type="image">🖼️ Images <span class="count">(0)</span></button>
599
+ </div>
600
+ <div class="filter-actions">
601
+ <button class="filter-action-btn" id="selectAll">All</button>
602
+ <button class="filter-action-btn" id="selectNone">None</button>
603
+ <button class="filter-action-btn" id="closeFilters" title="Close filters">✕</button>
604
+ </div>
605
+ </div>
606
+
445
607
  {% if sessions and sessions|length > 1 %}
446
608
  <div class='navigation'>
447
609
  <h2>Sessions</h2>
@@ -503,13 +665,21 @@
503
665
  {% endif %}
504
666
  {% endfor %}
505
667
 
668
+ <button class="filter-messages" id="filterMessages" title="Toggle filters">🔍</button>
506
669
  <button class="toggle-details" id="toggleDetails" title="Toggle all details">📋</button>
507
670
  <a class="scroll-top" title="Scroll to top" href="#title">🔝</a>
508
671
 
509
672
  <script>
510
673
  document.addEventListener('DOMContentLoaded', function () {
511
674
  const toggleButton = document.getElementById('toggleDetails');
512
-
675
+ const filterButton = document.getElementById('filterMessages');
676
+ const filterToolbar = document.querySelector('.filter-toolbar');
677
+ const selectAllButton = document.getElementById('selectAll');
678
+ const selectNoneButton = document.getElementById('selectNone');
679
+ const closeFiltersButton = document.getElementById('closeFilters');
680
+ const filterToggles = document.querySelectorAll('.filter-toggle');
681
+
682
+ // Toggle details functionality
513
683
  function updateToggleButton() {
514
684
  const allDetails = document.querySelectorAll('details.collapsible-details');
515
685
  const openCount = document.querySelectorAll('details[open].collapsible-details').length;
@@ -544,8 +714,134 @@
544
714
 
545
715
  toggleButton.addEventListener('click', toggleAllDetails);
546
716
 
547
- // Initialize button state
717
+ // Filter toolbar toggle functionality
718
+ function toggleFilterToolbar() {
719
+ const isVisible = filterToolbar.classList.contains('visible');
720
+ if (isVisible) {
721
+ filterToolbar.classList.remove('visible');
722
+ filterButton.classList.remove('active');
723
+ filterButton.title = 'Show filters';
724
+ } else {
725
+ filterToolbar.classList.add('visible');
726
+ filterButton.classList.add('active');
727
+ filterButton.title = 'Hide filters';
728
+ }
729
+ }
730
+
731
+ filterButton.addEventListener('click', toggleFilterToolbar);
732
+ closeFiltersButton.addEventListener('click', toggleFilterToolbar);
733
+
734
+ // Count messages by type and update button labels
735
+ function updateMessageCounts() {
736
+ const messageTypes = ['user', 'assistant', 'system', 'tool_use', 'tool_result', 'thinking', 'image'];
737
+
738
+ messageTypes.forEach(type => {
739
+ const messages = document.querySelectorAll(`.message.${type}:not(.session-header)`);
740
+ const count = messages.length;
741
+ const toggle = document.querySelector(`[data-type="${type}"]`);
742
+ const countSpan = toggle.querySelector('.count');
743
+
744
+ if (countSpan) {
745
+ countSpan.textContent = `(${count})`;
746
+
747
+ // Hide toggles for message types with 0 count
748
+ if (count === 0) {
749
+ toggle.style.display = 'none';
750
+ } else {
751
+ toggle.style.display = 'flex';
752
+ }
753
+ }
754
+ });
755
+ }
756
+
757
+ // Filter functionality
758
+ function applyFilter() {
759
+ const activeTypes = Array.from(filterToggles)
760
+ .filter(toggle => toggle.classList.contains('active'))
761
+ .map(toggle => toggle.dataset.type);
762
+
763
+ // Show/hide messages based on active toggle buttons
764
+ const allMessages = document.querySelectorAll('.message:not(.session-header)');
765
+ allMessages.forEach(message => {
766
+ const shouldShow = activeTypes.some(type => message.classList.contains(type));
767
+ if (shouldShow) {
768
+ message.classList.remove('filtered-hidden');
769
+ } else {
770
+ message.classList.add('filtered-hidden');
771
+ }
772
+ });
773
+
774
+ // Update visible counts in real-time
775
+ updateVisibleCounts();
776
+
777
+ // Update filter button appearance based on whether all types are selected
778
+ const allTypesSelected = activeTypes.length === filterToggles.length;
779
+ if (!allTypesSelected && filterToolbar.classList.contains('visible')) {
780
+ filterButton.classList.add('active');
781
+ } else if (allTypesSelected && filterToolbar.classList.contains('visible')) {
782
+ filterButton.classList.add('active');
783
+ }
784
+ }
785
+
786
+ function updateVisibleCounts() {
787
+ const messageTypes = ['user', 'assistant', 'system', 'tool_use', 'tool_result', 'thinking', 'image'];
788
+
789
+ messageTypes.forEach(type => {
790
+ const visibleMessages = document.querySelectorAll(`.message.${type}:not(.session-header):not(.filtered-hidden)`);
791
+ const totalMessages = document.querySelectorAll(`.message.${type}:not(.session-header)`);
792
+ const visibleCount = visibleMessages.length;
793
+ const totalCount = totalMessages.length;
794
+
795
+ const toggle = document.querySelector(`[data-type="${type}"]`);
796
+ const countSpan = toggle.querySelector('.count');
797
+
798
+ if (countSpan && totalCount > 0) {
799
+ // Show "visible/total" format when filtering is active
800
+ const activeTypes = Array.from(filterToggles)
801
+ .filter(toggle => toggle.classList.contains('active'))
802
+ .map(toggle => toggle.dataset.type);
803
+
804
+ const isFiltering = activeTypes.length < filterToggles.length;
805
+
806
+ if (isFiltering && visibleCount !== totalCount) {
807
+ countSpan.textContent = `(${visibleCount}/${totalCount})`;
808
+ } else {
809
+ countSpan.textContent = `(${totalCount})`;
810
+ }
811
+ }
812
+ });
813
+ }
814
+
815
+ function toggleFilter(button) {
816
+ button.classList.toggle('active');
817
+ applyFilter();
818
+ }
819
+
820
+ function selectAllTypes() {
821
+ filterToggles.forEach(toggle => {
822
+ toggle.classList.add('active');
823
+ });
824
+ applyFilter();
825
+ }
826
+
827
+ function selectNoTypes() {
828
+ filterToggles.forEach(toggle => {
829
+ toggle.classList.remove('active');
830
+ });
831
+ applyFilter();
832
+ }
833
+
834
+ // Event listeners for filter toggles
835
+ filterToggles.forEach(toggle => {
836
+ toggle.addEventListener('click', () => toggleFilter(toggle));
837
+ });
838
+
839
+ selectAllButton.addEventListener('click', selectAllTypes);
840
+ selectNoneButton.addEventListener('click', selectNoTypes);
841
+
842
+ // Initialize button state and message counts
548
843
  updateToggleButton();
844
+ updateMessageCounts();
549
845
  });
550
846
  </script>
551
847
  </body>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "claude-code-log"
3
- version = "0.2.7"
3
+ version = "0.2.8"
4
4
  description = "Convert Claude Code transcript JSONL files to HTML"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -13,7 +13,7 @@ wheels = [
13
13
 
14
14
  [[package]]
15
15
  name = "claude-code-log"
16
- version = "0.2.7"
16
+ version = "0.2.8"
17
17
  source = { editable = "." }
18
18
  dependencies = [
19
19
  { name = "click" },
File without changes