claude-code-log 0.2.2__tar.gz → 0.2.3__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 (42) hide show
  1. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/.claude/settings.local.json +5 -1
  2. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/CHANGELOG.md +6 -0
  3. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/PKG-INFO +8 -6
  4. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/README.md +7 -5
  5. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/claude_code_log/converter.py +6 -1
  6. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/claude_code_log/models.py +0 -29
  7. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/claude_code_log/parser.py +4 -3
  8. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/claude_code_log/renderer.py +58 -33
  9. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/claude_code_log/templates/transcript.html +80 -0
  10. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/pyproject.toml +2 -1
  11. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/scripts/style_guide_output/index_style_guide.html +69 -45
  12. claude_code_log-0.2.3/scripts/style_guide_output/transcript_style_guide.html +689 -0
  13. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/test/test_command_handling.py +3 -1
  14. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/test/test_data/edge_cases.jsonl +6 -0
  15. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/test/test_template_rendering.py +2 -1
  16. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/test/test_todowrite_rendering.py +1 -1
  17. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/uv.lock +12 -1
  18. claude_code_log-0.2.2/scripts/style_guide_output/transcript_style_guide.html +0 -430
  19. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/.github/workflows/ci.yml +0 -0
  20. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/.gitignore +0 -0
  21. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/CLAUDE.md +0 -0
  22. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/LICENSE +0 -0
  23. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/claude_code_log/__init__.py +0 -0
  24. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/claude_code_log/cli.py +0 -0
  25. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/claude_code_log/py.typed +0 -0
  26. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/claude_code_log/templates/index.html +0 -0
  27. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/justfile +0 -0
  28. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/scripts/generate_style_guide.py +0 -0
  29. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/scripts/style_guide_output/index.html +0 -0
  30. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/test/README.md +0 -0
  31. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/test/__init__.py +0 -0
  32. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/test/test_data/representative_messages.jsonl +0 -0
  33. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/test/test_data/session_b.jsonl +0 -0
  34. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/test/test_data/todowrite_examples.jsonl +0 -0
  35. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/test/test_date_filtering.py +0 -0
  36. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/test/test_filtering.py +0 -0
  37. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/test/test_markdown_rendering.py +0 -0
  38. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/test/test_message_filtering.py +0 -0
  39. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/test/test_message_types.py +0 -0
  40. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/test/test_path_conversion.py +0 -0
  41. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/test/test_template_data.py +0 -0
  42. {claude_code_log-0.2.2 → claude_code_log-0.2.3}/test/test_template_utils.py +0 -0
@@ -24,7 +24,11 @@
24
24
  "Bash(rg:*)",
25
25
  "Bash(act:*)",
26
26
  "Bash(just render-test-data:*)",
27
- "Bash(find:*)"
27
+ "Bash(find:*)",
28
+ "Bash(open /tmp/test_output.html)",
29
+ "Bash(open /Users/dain/workspace/claude-code-log/scripts/style_guide_output/transcript_style_guide.html)",
30
+ "Bash(open /tmp/test_preview.html)",
31
+ "Bash(open /tmp/test_improved_preview.html)"
28
32
  ],
29
33
  "deny": []
30
34
  }
@@ -5,6 +5,12 @@ All notable changes to claude-code-log will be documented in this file.
5
5
  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
+ ## [0.2.3] - 2025-06-16
9
+
10
+ ### Changed
11
+
12
+ - **Error handling**: Add more detailed error handling
13
+
8
14
  ## [0.2.2] - 2025-06-16
9
15
 
10
16
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-code-log
3
- Version: 0.2.2
3
+ Version: 0.2.3
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
@@ -21,6 +21,8 @@ Description-Content-Type: text/markdown
21
21
 
22
22
  A Python CLI tool that converts Claude Code transcript JSONL files into readable HTML format.
23
23
 
24
+ [claude_code_log.webm](https://github.com/user-attachments/assets/12d94faf-6901-4429-b4e6-ea5f102d0c1c)
25
+
24
26
  ## Project Overview
25
27
 
26
28
  📋 **[View Changelog](CHANGELOG.md)** - See what's new in each release
@@ -41,11 +43,10 @@ uvx claude-code-log --open-browser
41
43
  - **Single File or Directory Processing**: Convert individual JSONL files or specific directories
42
44
  - **Chronological Ordering**: All messages sorted by timestamp across sessions
43
45
  - **Session Demarcation**: Clear visual separators between different transcript sessions
44
- - **User-Focused**: Shows only user messages by default (assistant responses filtered out)
45
46
  - **Date Range Filtering**: Filter messages by date range using natural language (e.g., "today", "yesterday", "last week")
46
- - **Summary Support**: Display summary messages with highlighted formatting
47
+ - **Summary Support**: Display summary messages matched with corresponding sessions
47
48
  - **System Command Visibility**: Show system commands (like `init`) in expandable details
48
- - **Markdown Rendering**: Automatic markdown rendering in message content using marked.js
49
+ - **Markdown Rendering**: Automatic markdown rendering in assistant message content
49
50
  - **Project Navigation**: Master index page with project statistics and quick navigation
50
51
  - **Space-Efficient Layout**: Compact design optimized for content density
51
52
  - **CLI Interface**: Simple command-line tool using Click
@@ -116,7 +117,7 @@ The project uses:
116
117
  - dateparser for natural language date parsing
117
118
  - Standard library for JSON/HTML processing
118
119
  - Minimal dependencies for portability
119
- - marked.js (CDN) for client-side markdown rendering
120
+ - mistune for quick Markdown rendering
120
121
 
121
122
  ## Development Commands
122
123
 
@@ -232,6 +233,7 @@ uv run claude-code-log
232
233
 
233
234
  ## TODO
234
235
 
235
- - Show top level stats on index page token usage added up + time of last interaction
236
+ - **Show top level stats on index page**: token usage added up + time of last interaction
236
237
  - **Tool Use Preview**: Show first few lines of tool use and other collapsed details
237
238
  - **In-page Filtering**: Client-side filtering and search
239
+ - **Timeline view**: Show interaction on a timeline to get a better idea on timings and parallel calls
@@ -2,6 +2,8 @@
2
2
 
3
3
  A Python CLI tool that converts Claude Code transcript JSONL files into readable HTML format.
4
4
 
5
+ [claude_code_log.webm](https://github.com/user-attachments/assets/12d94faf-6901-4429-b4e6-ea5f102d0c1c)
6
+
5
7
  ## Project Overview
6
8
 
7
9
  📋 **[View Changelog](CHANGELOG.md)** - See what's new in each release
@@ -22,11 +24,10 @@ uvx claude-code-log --open-browser
22
24
  - **Single File or Directory Processing**: Convert individual JSONL files or specific directories
23
25
  - **Chronological Ordering**: All messages sorted by timestamp across sessions
24
26
  - **Session Demarcation**: Clear visual separators between different transcript sessions
25
- - **User-Focused**: Shows only user messages by default (assistant responses filtered out)
26
27
  - **Date Range Filtering**: Filter messages by date range using natural language (e.g., "today", "yesterday", "last week")
27
- - **Summary Support**: Display summary messages with highlighted formatting
28
+ - **Summary Support**: Display summary messages matched with corresponding sessions
28
29
  - **System Command Visibility**: Show system commands (like `init`) in expandable details
29
- - **Markdown Rendering**: Automatic markdown rendering in message content using marked.js
30
+ - **Markdown Rendering**: Automatic markdown rendering in assistant message content
30
31
  - **Project Navigation**: Master index page with project statistics and quick navigation
31
32
  - **Space-Efficient Layout**: Compact design optimized for content density
32
33
  - **CLI Interface**: Simple command-line tool using Click
@@ -97,7 +98,7 @@ The project uses:
97
98
  - dateparser for natural language date parsing
98
99
  - Standard library for JSON/HTML processing
99
100
  - Minimal dependencies for portability
100
- - marked.js (CDN) for client-side markdown rendering
101
+ - mistune for quick Markdown rendering
101
102
 
102
103
  ## Development Commands
103
104
 
@@ -213,6 +214,7 @@ uv run claude-code-log
213
214
 
214
215
  ## TODO
215
216
 
216
- - Show top level stats on index page token usage added up + time of last interaction
217
+ - **Show top level stats on index page**: token usage added up + time of last interaction
217
218
  - **Tool Use Preview**: Show first few lines of tool use and other collapsed details
218
219
  - **In-page Filtering**: Client-side filtering and search
220
+ - **Timeline view**: Show interaction on a timeline to get a better idea on timings and parallel calls
@@ -2,6 +2,7 @@
2
2
  """Convert Claude transcript JSONL files to HTML."""
3
3
 
4
4
  from pathlib import Path
5
+ import traceback
5
6
  from typing import List, Optional, Dict, Any
6
7
 
7
8
  from .parser import (
@@ -107,7 +108,11 @@ def process_projects_hierarchy(
107
108
  }
108
109
  )
109
110
  except Exception as e:
110
- print(f"Warning: Failed to process {project_dir}: {e}")
111
+ print(
112
+ f"Warning: Failed to process {project_dir}: {e}\n"
113
+ f"Previous (in alphabetical order) file before error: {project_summaries[-1]}\n"
114
+ f"Traceback:\n{traceback.format_exc()}"
115
+ )
111
116
  continue
112
117
 
113
118
  # Generate index HTML
@@ -215,32 +215,3 @@ def parse_transcript_entry(data: Dict[str, Any]) -> TranscriptEntry:
215
215
  return SummaryTranscriptEntry.model_validate(data)
216
216
  else:
217
217
  raise ValueError(f"Unknown transcript entry type: {entry_type}")
218
-
219
-
220
- def try_parse_transcript_entry(entry_dict: Dict[str, Any]) -> Optional[TranscriptEntry]:
221
- """Try to parse a transcript entry, returning None if parsing fails."""
222
- try:
223
- return parse_transcript_entry(entry_dict)
224
- except Exception:
225
- return None
226
-
227
-
228
- def parse_transcript_entry_with_fallback(
229
- entry_dict: Dict[str, Any],
230
- ) -> Optional[TranscriptEntry]:
231
- """
232
- Try to parse a transcript entry against different Pydantic models.
233
-
234
- This function attempts to parse the entry using each model type in order,
235
- using proper content parsing to avoid Union validation errors.
236
-
237
- Args:
238
- entry_dict: Dictionary parsed from JSON
239
-
240
- Returns:
241
- The appropriate TranscriptEntry subclass, or None if parsing fails
242
- """
243
- try:
244
- return parse_transcript_entry(entry_dict)
245
- except Exception:
246
- return None
@@ -3,6 +3,7 @@
3
3
 
4
4
  import json
5
5
  from pathlib import Path
6
+ import traceback
6
7
  from typing import List, Optional, Union, Dict
7
8
  from datetime import datetime
8
9
  import dateparser
@@ -109,7 +110,7 @@ def load_transcript(jsonl_path: Path) -> List[TranscriptEntry]:
109
110
  if line:
110
111
  try:
111
112
  entry_dict = json.loads(line)
112
- entry_type = entry_dict.get("type", "unknown")
113
+ entry_type = entry_dict.get("type", "unknown, missing type")
113
114
 
114
115
  if entry_type in ["user", "assistant", "summary"]:
115
116
  # Parse using Pydantic models
@@ -121,7 +122,7 @@ def load_transcript(jsonl_path: Path) -> List[TranscriptEntry]:
121
122
  unhandled_types.get(entry_type, 0) + 1
122
123
  )
123
124
  except json.JSONDecodeError as e:
124
- error_key = f"JSON decode error: {type(e).__name__}"
125
+ error_key = f"JSON decode error: {str(e)}"
125
126
  unique_errors[error_key] = unique_errors.get(error_key, 0) + 1
126
127
  except ValueError as e:
127
128
  # Extract a more descriptive error message
@@ -132,7 +133,7 @@ def load_transcript(jsonl_path: Path) -> List[TranscriptEntry]:
132
133
  error_key = f"ValueError: {error_msg[:200]}..."
133
134
  unique_errors[error_key] = unique_errors.get(error_key, 0) + 1
134
135
  except Exception as e:
135
- error_key = f"Unexpected error: {type(e).__name__}"
136
+ error_key = f"Unexpected error: {str(e)}\n{traceback.format_exc()}"
136
137
  unique_errors[error_key] = unique_errors.get(error_key, 0) + 1
137
138
 
138
139
  # Print summary of errors if any occurred
@@ -37,6 +37,41 @@ def escape_html(text: str) -> str:
37
37
  return html.escape(text)
38
38
 
39
39
 
40
+ def create_collapsible_details(
41
+ summary: str, content: str, css_classes: str = ""
42
+ ) -> str:
43
+ """Create a collapsible details element with consistent styling and preview functionality."""
44
+ class_attr = ' class="collapsible-details"'
45
+ wrapper_classes = f"tool-content{' ' + css_classes if css_classes else ''}"
46
+
47
+ # Extract first few lines for preview (up to ~3-5 lines, roughly 200 chars)
48
+ import re
49
+
50
+ # Remove HTML tags and get plain text for preview
51
+ plain_text = re.sub(r"<[^>]+>", "", content)
52
+ # Get first ~200 characters, break at word boundaries
53
+ preview_text = plain_text[:200]
54
+ if len(plain_text) > 200:
55
+ preview_text = preview_text.rsplit(" ", 1)[0] + "..."
56
+
57
+ # Render preview as markdown for better formatting
58
+ preview_html = render_markdown(preview_text)
59
+
60
+ return f"""
61
+ <div class="{wrapper_classes}">
62
+ <details{class_attr}>
63
+ <summary>
64
+ {summary}
65
+ <div class="preview-content">{preview_html}</div>
66
+ </summary>
67
+ <div class="details-content">
68
+ {content}
69
+ </div>
70
+ </details>
71
+ </div>
72
+ """
73
+
74
+
40
75
  def render_markdown(text: str) -> str:
41
76
  """Convert markdown text to HTML using mistune."""
42
77
  # Configure mistune with GitHub-flavored markdown features
@@ -172,18 +207,16 @@ def format_tool_use_content(tool_use: ToolUseContent) -> str:
172
207
  except (TypeError, ValueError):
173
208
  escaped_input = escape_html(str(tool_use.input))
174
209
 
175
- return f"""
176
- <div class="tool-content tool-use">
177
- <details>
178
- <summary><strong>🛠️ Tool Use:</strong> {escaped_name} (ID: {escaped_id})</summary>
179
- <div class="tool-input">
180
- <strong>Input:</strong>
181
- <pre>{escaped_input}</pre>
182
- </div>
183
- </details>
184
- </div>
210
+ summary = f"<strong>🛠️ Tool Use:</strong> {escaped_name} (ID: {escaped_id})"
211
+ content = f"""
212
+ <div class="tool-input">
213
+ <strong>Input:</strong>
214
+ <pre>{escaped_input}</pre>
215
+ </div>
185
216
  """
186
217
 
218
+ return create_collapsible_details(summary, content, "tool-use")
219
+
187
220
 
188
221
  def format_tool_result_content(tool_result: ToolResultContent) -> str:
189
222
  """Format tool result content as HTML."""
@@ -197,37 +230,27 @@ def format_tool_result_content(tool_result: ToolResultContent) -> str:
197
230
  content_parts: List[str] = []
198
231
  for item in tool_result.content:
199
232
  if item.get("type") == "text":
200
- content_parts.append(item.get("text", ""))
233
+ text_value = item.get("text")
234
+ if isinstance(text_value, str):
235
+ content_parts.append(text_value)
201
236
  escaped_content = escape_html("\n".join(content_parts))
202
237
 
203
238
  error_indicator = " (🚨 Error)" if tool_result.is_error else ""
204
239
 
205
- return f"""
206
- <div class="tool-content tool-result">
207
- <details>
208
- <summary><strong>🧰 Tool Result{error_indicator}:</strong> {escaped_id}</summary>
209
- <div class="tool-input">
210
- <pre>{escaped_content}</pre>
211
- </div>
212
- </details>
213
- </div>
214
- """
240
+ summary = f"<strong>🧰 Tool Result{error_indicator}:</strong> {escaped_id}"
241
+ content = f'<div class="tool-input"><pre>{escaped_content}</pre></div>'
242
+
243
+ return create_collapsible_details(summary, content, "tool-result")
215
244
 
216
245
 
217
246
  def format_thinking_content(thinking: ThinkingContent) -> str:
218
247
  """Format thinking content as HTML."""
219
248
  escaped_thinking = escape_html(thinking.thinking)
220
249
 
221
- return f"""
222
- <div class="tool-content thinking-content">
223
- <details>
224
- <summary><strong>💭 Thinking</strong></summary>
225
- <div class="thinking-text">
226
- <pre>{escaped_thinking}</pre>
227
- </div>
228
- </details>
229
- </div>
230
- """
250
+ summary = "<strong>💭 Thinking</strong>"
251
+ content = f'<div class="thinking-text"><pre>{escaped_thinking}</pre></div>'
252
+
253
+ return create_collapsible_details(summary, content, "thinking-content")
231
254
 
232
255
 
233
256
  def format_image_content(image: ImageContent) -> str:
@@ -595,9 +618,11 @@ def generate_html(messages: List[TranscriptEntry], title: Optional[str] = None)
595
618
  if command_args:
596
619
  content_parts.append(f"<strong>Args:</strong> {escaped_command_args}")
597
620
  if command_contents:
598
- content_parts.append(
599
- f"<details><summary>Content</summary><div class='content'>{escaped_command_contents}</div></details>"
621
+ details_content = (
622
+ f"<div class='content'>{escaped_command_contents}</div>"
600
623
  )
624
+ details_html = create_collapsible_details("Content", details_content)
625
+ content_parts.append(details_html)
601
626
 
602
627
  content_html = "<br>".join(content_parts)
603
628
  message_type = "system"
@@ -60,6 +60,86 @@
60
60
  margin-bottom: 8px;
61
61
  }
62
62
 
63
+ /* Collapsible details preview functionality */
64
+ .collapsible-details {
65
+ position: relative;
66
+ }
67
+
68
+ .collapsible-details summary {
69
+ position: relative;
70
+ cursor: pointer;
71
+ }
72
+
73
+ /* Preview content styling - shown when closed */
74
+ .collapsible-details:not([open]) .preview-content {
75
+ display: block;
76
+ margin-top: 8px;
77
+ padding: 6px 8px;
78
+ background-color: rgba(255, 255, 255, 0.3);
79
+ border-radius: 3px;
80
+ font-size: 0.9em;
81
+ font-weight: normal;
82
+ color: #555;
83
+ line-height: 1.3;
84
+ white-space: pre-wrap;
85
+ word-wrap: break-word;
86
+ position: relative;
87
+ max-height: 4em;
88
+ overflow: hidden;
89
+ }
90
+
91
+ /* Add subtle fade effect to preview content */
92
+ .collapsible-details:not([open]) .preview-content::after {
93
+ content: "";
94
+ position: absolute;
95
+ bottom: 0;
96
+ left: 0;
97
+ right: 0;
98
+ height: 1em;
99
+ background: linear-gradient(transparent, rgba(255, 255, 255, 0.3));
100
+ pointer-events: none;
101
+ }
102
+
103
+ /* Hide preview content when details is open */
104
+ .collapsible-details[open] .preview-content {
105
+ display: none;
106
+ }
107
+
108
+ /* Style the full details content to match preview */
109
+ .collapsible-details .details-content {
110
+ margin-top: 8px;
111
+ padding: 6px 8px;
112
+ background-color: rgba(255, 255, 255, 0.3);
113
+ border-radius: 3px;
114
+ font-size: 0.9em;
115
+ font-weight: normal;
116
+ color: #555;
117
+ line-height: 1.3;
118
+ }
119
+
120
+ /* Hide details content when closed */
121
+ .collapsible-details:not([open]) .details-content {
122
+ display: none;
123
+ }
124
+
125
+ /* Remove extra styling from nested elements */
126
+ .collapsible-details .details-content .tool-input {
127
+ background-color: transparent;
128
+ border: none;
129
+ box-shadow: none;
130
+ padding: 0;
131
+ margin: 0;
132
+ }
133
+
134
+ .collapsible-details .details-content pre {
135
+ background-color: transparent;
136
+ padding: 0;
137
+ margin: 0;
138
+ font-family: inherit;
139
+ font-size: inherit;
140
+ color: inherit;
141
+ }
142
+
63
143
  .tool-content {
64
144
  background-color: #f8f9fa66;
65
145
  border-radius: 4px;
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "claude-code-log"
3
- version = "0.2.2"
3
+ version = "0.2.3"
4
4
  description = "Convert Claude Code transcript JSONL files to HTML"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -95,4 +95,5 @@ dev = [
95
95
  "ruff>=0.11.2",
96
96
  "pytest-xdist>=3.6.1",
97
97
  "pyright>=1.1.350",
98
+ "vulture>=2.14",
98
99
  ]
@@ -1,5 +1,6 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang='en'>
3
+
3
4
  <head>
4
5
  <meta charset='UTF-8'>
5
6
  <meta name='viewport' content='width=device-width, initial-scale=1.0'>
@@ -11,43 +12,54 @@
11
12
  max-width: 1200px;
12
13
  margin: 0 auto;
13
14
  padding: 20px;
14
- background-color: #fafafa;
15
+ background: linear-gradient(90deg, #f3d6d2, #f1dcce, #f0e4ca, #eeecc7, #e3ecc3, #d5eac0, #c6e8bd, #b9e6bc, #b6e3c5, #b3e1cf);
15
16
  color: #333;
16
17
  }
18
+
17
19
  h1 {
18
20
  text-align: center;
19
21
  color: #2c3e50;
20
22
  margin-bottom: 30px;
21
23
  font-size: 2em;
22
24
  }
25
+
23
26
  .project-list {
24
27
  display: grid;
25
28
  gap: 15px;
26
29
  }
30
+
27
31
  .project-card {
28
- background: white;
32
+ background-color: #ffffff66;
29
33
  border-radius: 8px;
30
34
  padding: 20px;
31
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
32
- border-left: 4px solid #2196f3;
35
+ box-shadow: -7px -7px 10px #eeeeee44, 7px 7px 10px #00000011;
36
+ border-left: #ffffff66 1px solid;
37
+ border-top: #ffffff66 1px solid;
38
+ border-bottom: #00000017 1px solid;
39
+ border-right: #00000017 1px solid;
33
40
  }
41
+
34
42
  .project-card:hover {
35
- box-shadow: 0 4px 8px rgba(0,0,0,0.15);
43
+ box-shadow: -10px -10px 15px #eeeeee66, 10px 10px 15px #00000022;
36
44
  transform: translateY(-1px);
37
45
  transition: all 0.2s ease;
38
46
  }
47
+
39
48
  .project-name {
40
49
  font-size: 1.2em;
41
50
  font-weight: 600;
42
51
  margin-bottom: 10px;
43
52
  }
53
+
44
54
  .project-name a {
45
55
  text-decoration: none;
46
56
  color: #2196f3;
47
57
  }
58
+
48
59
  .project-name a:hover {
49
60
  text-decoration: underline;
50
61
  }
62
+
51
63
  .project-stats {
52
64
  color: #666;
53
65
  font-size: 0.9em;
@@ -55,42 +67,53 @@
55
67
  gap: 20px;
56
68
  flex-wrap: wrap;
57
69
  }
70
+
58
71
  .stat {
59
72
  display: flex;
60
73
  align-items: center;
61
74
  gap: 5px;
62
75
  }
76
+
63
77
  .summary {
64
78
  text-align: center;
65
79
  margin-bottom: 30px;
66
80
  padding: 15px;
67
- background: white;
81
+ background-color: #ffffff66;
68
82
  border-radius: 8px;
69
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
83
+ box-shadow: -7px -7px 10px #eeeeee44, 7px 7px 10px #00000011;
84
+ border-left: #ffffff66 1px solid;
85
+ border-top: #ffffff66 1px solid;
86
+ border-bottom: #00000017 1px solid;
87
+ border-right: #00000017 1px solid;
70
88
  }
89
+
71
90
  .summary-stats {
72
91
  display: flex;
73
92
  justify-content: center;
74
93
  gap: 30px;
75
94
  flex-wrap: wrap;
76
95
  }
96
+
77
97
  .summary-stat {
78
98
  text-align: center;
79
99
  }
100
+
80
101
  .summary-stat .number {
81
102
  font-size: 1.5em;
82
103
  font-weight: 600;
83
104
  color: #2196f3;
84
105
  }
106
+
85
107
  .summary-stat .label {
86
108
  color: #666;
87
109
  font-size: 0.9em;
88
110
  }
89
111
  </style>
90
112
  </head>
113
+
91
114
  <body>
92
115
  <h1>Claude Code Projects (from last week to today)</h1>
93
-
116
+
94
117
  <div class='summary'>
95
118
  <div class='summary-stats'>
96
119
  <div class='summary-stat'>
@@ -107,53 +130,54 @@
107
130
  </div>
108
131
  </div>
109
132
  </div>
110
-
133
+
111
134
  <div class='project-list'>
112
135
 
113
- <div class='project-card'>
114
- <div class='project-name'>
115
- <a href='-org-internal-tools-automation/combined_transcripts.html'>org/internal/tools/automation</a>
116
- </div>
117
- <div class='project-stats'>
118
- <div class='stat'>📁 12 transcript files</div>
119
- <div class='stat'>💬 445 messages</div>
120
- <div class='stat'>🕒 2023-11-15 19:03</div>
121
- </div>
136
+ <div class='project-card'>
137
+ <div class='project-name'>
138
+ <a href='-org-internal-tools-automation/combined_transcripts.html'>org/internal/tools/automation</a>
122
139
  </div>
140
+ <div class='project-stats'>
141
+ <div class='stat'>📁 12 transcript files</div>
142
+ <div class='stat'>💬 445 messages</div>
143
+ <div class='stat'>🕒 2023-11-15 19:03</div>
144
+ </div>
145
+ </div>
123
146
 
124
- <div class='project-card'>
125
- <div class='project-name'>
126
- <a href='-user-workspace-my-web-app/combined_transcripts.html'>user/workspace/my/web/app</a>
127
- </div>
128
- <div class='project-stats'>
129
- <div class='stat'>📁 8 transcript files</div>
130
- <div class='stat'>💬 203 messages</div>
131
- <div class='stat'>🕒 2023-11-15 12:06</div>
132
- </div>
147
+ <div class='project-card'>
148
+ <div class='project-name'>
149
+ <a href='-user-workspace-my-web-app/combined_transcripts.html'>user/workspace/my/web/app</a>
133
150
  </div>
151
+ <div class='project-stats'>
152
+ <div class='stat'>📁 8 transcript files</div>
153
+ <div class='stat'>💬 203 messages</div>
154
+ <div class='stat'>🕒 2023-11-15 12:06</div>
155
+ </div>
156
+ </div>
134
157
 
135
- <div class='project-card'>
136
- <div class='project-name'>
137
- <a href='data-analysis-project/combined_transcripts.html'>data-analysis-project</a>
138
- </div>
139
- <div class='project-stats'>
140
- <div class='stat'>📁 3 transcript files</div>
141
- <div class='stat'>💬 89 messages</div>
142
- <div class='stat'>🕒 2023-11-15 05:10</div>
143
- </div>
158
+ <div class='project-card'>
159
+ <div class='project-name'>
160
+ <a href='data-analysis-project/combined_transcripts.html'>data-analysis-project</a>
144
161
  </div>
162
+ <div class='project-stats'>
163
+ <div class='stat'>📁 3 transcript files</div>
164
+ <div class='stat'>💬 89 messages</div>
165
+ <div class='stat'>🕒 2023-11-15 05:10</div>
166
+ </div>
167
+ </div>
145
168
 
146
- <div class='project-card'>
147
- <div class='project-name'>
148
- <a href='claude-code-assistant/combined_transcripts.html'>claude-code-assistant</a>
149
- </div>
150
- <div class='project-stats'>
151
- <div class='stat'>📁 5 transcript files</div>
152
- <div class='stat'>💬 127 messages</div>
153
- <div class='stat'>🕒 2023-11-14 22:13</div>
154
- </div>
169
+ <div class='project-card'>
170
+ <div class='project-name'>
171
+ <a href='claude-code-assistant/combined_transcripts.html'>claude-code-assistant</a>
155
172
  </div>
173
+ <div class='project-stats'>
174
+ <div class='stat'>📁 5 transcript files</div>
175
+ <div class='stat'>💬 127 messages</div>
176
+ <div class='stat'>🕒 2023-11-14 22:13</div>
177
+ </div>
178
+ </div>
156
179
 
157
180
  </div>
158
181
  </body>
182
+
159
183
  </html>