claude-code-log 0.2.3__tar.gz → 0.2.4__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.
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/.claude/settings.local.json +2 -1
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/CHANGELOG.md +6 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/PKG-INFO +1 -1
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/claude_code_log/models.py +17 -14
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/claude_code_log/parser.py +32 -30
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/claude_code_log/renderer.py +63 -79
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/claude_code_log/templates/transcript.html +0 -80
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/pyproject.toml +1 -1
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/scripts/style_guide_output/index_style_guide.html +45 -69
- claude_code_log-0.2.4/scripts/style_guide_output/transcript_style_guide.html +430 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/test/test_command_handling.py +1 -3
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/test/test_data/edge_cases.jsonl +3 -2
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/test/test_template_rendering.py +1 -2
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/test/test_todowrite_rendering.py +1 -1
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/uv.lock +1 -1
- claude_code_log-0.2.3/scripts/style_guide_output/transcript_style_guide.html +0 -689
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/.github/workflows/ci.yml +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/.gitignore +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/CLAUDE.md +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/LICENSE +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/README.md +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/claude_code_log/__init__.py +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/claude_code_log/cli.py +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/claude_code_log/converter.py +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/claude_code_log/py.typed +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/claude_code_log/templates/index.html +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/justfile +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/scripts/generate_style_guide.py +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/scripts/style_guide_output/index.html +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/test/README.md +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/test/__init__.py +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/test/test_data/representative_messages.jsonl +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/test/test_data/session_b.jsonl +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/test/test_data/todowrite_examples.jsonl +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/test/test_date_filtering.py +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/test/test_filtering.py +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/test/test_markdown_rendering.py +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/test/test_message_filtering.py +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/test/test_message_types.py +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/test/test_path_conversion.py +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/test/test_template_data.py +0 -0
- {claude_code_log-0.2.3 → claude_code_log-0.2.4}/test/test_template_utils.py +0 -0
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
"Bash(open /tmp/test_output.html)",
|
|
29
29
|
"Bash(open /Users/dain/workspace/claude-code-log/scripts/style_guide_output/transcript_style_guide.html)",
|
|
30
30
|
"Bash(open /tmp/test_preview.html)",
|
|
31
|
-
"Bash(open /tmp/test_improved_preview.html)"
|
|
31
|
+
"Bash(open /tmp/test_improved_preview.html)",
|
|
32
|
+
"Bash(open /tmp/test_fixed_preview.html)"
|
|
32
33
|
],
|
|
33
34
|
"deny": []
|
|
34
35
|
}
|
|
@@ -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.4] - 2025-06-18
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **More rrror handling**: Add better error reporting with line numbers and render fallbacks
|
|
13
|
+
|
|
8
14
|
## [0.2.3] - 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.
|
|
3
|
+
Version: 0.2.4
|
|
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
|
|
@@ -150,20 +150,23 @@ TranscriptEntry = Union[
|
|
|
150
150
|
|
|
151
151
|
def parse_content_item(item_data: Dict[str, Any]) -> ContentItem:
|
|
152
152
|
"""Parse a content item based on its type field."""
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
153
|
+
try:
|
|
154
|
+
content_type = item_data.get("type", "")
|
|
155
|
+
|
|
156
|
+
if content_type == "text":
|
|
157
|
+
return TextContent.model_validate(item_data)
|
|
158
|
+
elif content_type == "tool_use":
|
|
159
|
+
return ToolUseContent.model_validate(item_data)
|
|
160
|
+
elif content_type == "tool_result":
|
|
161
|
+
return ToolResultContent.model_validate(item_data)
|
|
162
|
+
elif content_type == "thinking":
|
|
163
|
+
return ThinkingContent.model_validate(item_data)
|
|
164
|
+
elif content_type == "image":
|
|
165
|
+
return ImageContent.model_validate(item_data)
|
|
166
|
+
else:
|
|
167
|
+
# Fallback to text content for unknown types
|
|
168
|
+
return TextContent(type="text", text=str(item_data))
|
|
169
|
+
except AttributeError:
|
|
167
170
|
return TextContent(type="text", text=str(item_data))
|
|
168
171
|
|
|
169
172
|
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
import json
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
import
|
|
7
|
-
from typing import List, Optional, Union
|
|
6
|
+
import re
|
|
7
|
+
from typing import Any, List, Optional, Union
|
|
8
8
|
from datetime import datetime
|
|
9
9
|
import dateparser
|
|
10
10
|
|
|
@@ -101,53 +101,55 @@ def filter_messages_by_date(
|
|
|
101
101
|
def load_transcript(jsonl_path: Path) -> List[TranscriptEntry]:
|
|
102
102
|
"""Load and parse JSONL transcript file."""
|
|
103
103
|
messages: List[TranscriptEntry] = []
|
|
104
|
-
unique_errors: Dict[str, int] = {}
|
|
105
|
-
unhandled_types: Dict[str, int] = {}
|
|
106
104
|
|
|
107
105
|
with open(jsonl_path, "r", encoding="utf-8") as f:
|
|
108
|
-
for line in f:
|
|
106
|
+
for line_no, line in enumerate(f):
|
|
109
107
|
line = line.strip()
|
|
110
108
|
if line:
|
|
111
109
|
try:
|
|
112
|
-
entry_dict = json.loads(line)
|
|
113
|
-
|
|
110
|
+
entry_dict: dict[str, Any] | str = json.loads(line)
|
|
111
|
+
if not isinstance(entry_dict, dict):
|
|
112
|
+
print(
|
|
113
|
+
f"Line {line_no} of {jsonl_path} is not a JSON object: {line}"
|
|
114
|
+
)
|
|
115
|
+
continue
|
|
116
|
+
|
|
117
|
+
entry_type: str | None = entry_dict.get("type")
|
|
114
118
|
|
|
115
119
|
if entry_type in ["user", "assistant", "summary"]:
|
|
116
120
|
# Parse using Pydantic models
|
|
117
121
|
entry = parse_transcript_entry(entry_dict)
|
|
118
122
|
messages.append(entry)
|
|
119
123
|
else:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
unhandled_types.get(entry_type, 0) + 1
|
|
124
|
+
print(
|
|
125
|
+
f"Line {line_no} of {jsonl_path} is not a recognised message type: {line}"
|
|
123
126
|
)
|
|
124
127
|
except json.JSONDecodeError as e:
|
|
125
|
-
|
|
126
|
-
|
|
128
|
+
print(
|
|
129
|
+
f"Line {line_no} of {jsonl_path} | JSON decode error: {str(e)}"
|
|
130
|
+
)
|
|
127
131
|
except ValueError as e:
|
|
128
132
|
# Extract a more descriptive error message
|
|
129
133
|
error_msg = str(e)
|
|
130
134
|
if "validation error" in error_msg.lower():
|
|
131
|
-
|
|
135
|
+
err_no_url = re.sub(
|
|
136
|
+
r" For further information visit https://errors.pydantic(.*)\n?",
|
|
137
|
+
"",
|
|
138
|
+
error_msg,
|
|
139
|
+
)
|
|
140
|
+
print(f"Line {line_no} of {jsonl_path} | {err_no_url}")
|
|
132
141
|
else:
|
|
133
|
-
|
|
134
|
-
|
|
142
|
+
print(
|
|
143
|
+
f"Line {line_no} of {jsonl_path} | ValueError: {error_msg}"
|
|
144
|
+
"\n{traceback.format_exc()}"
|
|
145
|
+
)
|
|
135
146
|
except Exception as e:
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if unhandled_types:
|
|
143
|
-
print("Unhandled message types:")
|
|
144
|
-
for msg_type, count in unhandled_types.items():
|
|
145
|
-
print(f" - {msg_type}: {count} occurrences")
|
|
146
|
-
if unique_errors:
|
|
147
|
-
print("Parsing errors:")
|
|
148
|
-
for error, count in unique_errors.items():
|
|
149
|
-
print(f" - {error}: {count} occurrences")
|
|
150
|
-
print()
|
|
147
|
+
print(
|
|
148
|
+
f"Line {line_no} of {jsonl_path} | Unexpected error: {str(e)}"
|
|
149
|
+
"\n{traceback.format_exc()}"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
print()
|
|
151
153
|
|
|
152
154
|
return messages
|
|
153
155
|
|
|
@@ -37,41 +37,6 @@ 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
|
-
|
|
75
40
|
def render_markdown(text: str) -> str:
|
|
76
41
|
"""Convert markdown text to HTML using mistune."""
|
|
77
42
|
# Configure mistune with GitHub-flavored markdown features
|
|
@@ -153,27 +118,36 @@ def format_todowrite_content(tool_use: ToolUseContent) -> str:
|
|
|
153
118
|
# Build todo list HTML
|
|
154
119
|
todo_items: List[str] = []
|
|
155
120
|
for todo in todos_data:
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
<
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
121
|
+
try:
|
|
122
|
+
todo_id = escape_html(str(todo.get("id", "")))
|
|
123
|
+
content = escape_html(str(todo.get("content", "")))
|
|
124
|
+
status = todo.get("status", "pending")
|
|
125
|
+
priority = todo.get("priority", "medium")
|
|
126
|
+
status_emoji = status_emojis.get(status, "⏳")
|
|
127
|
+
|
|
128
|
+
# Determine checkbox state
|
|
129
|
+
checked = "checked" if status == "completed" else ""
|
|
130
|
+
disabled = "disabled" if status == "completed" else ""
|
|
131
|
+
|
|
132
|
+
# CSS class for styling
|
|
133
|
+
item_class = f"todo-item {status} {priority}"
|
|
134
|
+
|
|
135
|
+
todo_items.append(f"""
|
|
136
|
+
<div class="{item_class}">
|
|
137
|
+
<input type="checkbox" {checked} {disabled} readonly>
|
|
138
|
+
<span class="todo-status">{status_emoji}</span>
|
|
139
|
+
<span class="todo-content">{content}</span>
|
|
140
|
+
<span class="todo-id">#{todo_id}</span>
|
|
141
|
+
</div>
|
|
142
|
+
""")
|
|
143
|
+
except AttributeError:
|
|
144
|
+
todo_items.append(f"""
|
|
145
|
+
<div class="todo-item pending medium">
|
|
146
|
+
<input type="checkbox" readonly>
|
|
147
|
+
<span class="todo-status">⏳</span>
|
|
148
|
+
<span class="todo-content">{str(todo)}</span>
|
|
149
|
+
</div>
|
|
150
|
+
""")
|
|
177
151
|
|
|
178
152
|
todos_html = "".join(todo_items)
|
|
179
153
|
|
|
@@ -207,16 +181,18 @@ def format_tool_use_content(tool_use: ToolUseContent) -> str:
|
|
|
207
181
|
except (TypeError, ValueError):
|
|
208
182
|
escaped_input = escape_html(str(tool_use.input))
|
|
209
183
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
<
|
|
213
|
-
<strong
|
|
214
|
-
<
|
|
215
|
-
|
|
184
|
+
return f"""
|
|
185
|
+
<div class="tool-content tool-use">
|
|
186
|
+
<details>
|
|
187
|
+
<summary><strong>🛠️ Tool Use:</strong> {escaped_name} (ID: {escaped_id})</summary>
|
|
188
|
+
<div class="tool-input">
|
|
189
|
+
<strong>Input:</strong>
|
|
190
|
+
<pre>{escaped_input}</pre>
|
|
191
|
+
</div>
|
|
192
|
+
</details>
|
|
193
|
+
</div>
|
|
216
194
|
"""
|
|
217
195
|
|
|
218
|
-
return create_collapsible_details(summary, content, "tool-use")
|
|
219
|
-
|
|
220
196
|
|
|
221
197
|
def format_tool_result_content(tool_result: ToolResultContent) -> str:
|
|
222
198
|
"""Format tool result content as HTML."""
|
|
@@ -230,27 +206,37 @@ def format_tool_result_content(tool_result: ToolResultContent) -> str:
|
|
|
230
206
|
content_parts: List[str] = []
|
|
231
207
|
for item in tool_result.content:
|
|
232
208
|
if item.get("type") == "text":
|
|
233
|
-
|
|
234
|
-
if isinstance(text_value, str):
|
|
235
|
-
content_parts.append(text_value)
|
|
209
|
+
content_parts.append(item.get("text", ""))
|
|
236
210
|
escaped_content = escape_html("\n".join(content_parts))
|
|
237
211
|
|
|
238
212
|
error_indicator = " (🚨 Error)" if tool_result.is_error else ""
|
|
239
213
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
214
|
+
return f"""
|
|
215
|
+
<div class="tool-content tool-result">
|
|
216
|
+
<details>
|
|
217
|
+
<summary><strong>🧰 Tool Result{error_indicator}:</strong> {escaped_id}</summary>
|
|
218
|
+
<div class="tool-input">
|
|
219
|
+
<pre>{escaped_content}</pre>
|
|
220
|
+
</div>
|
|
221
|
+
</details>
|
|
222
|
+
</div>
|
|
223
|
+
"""
|
|
244
224
|
|
|
245
225
|
|
|
246
226
|
def format_thinking_content(thinking: ThinkingContent) -> str:
|
|
247
227
|
"""Format thinking content as HTML."""
|
|
248
228
|
escaped_thinking = escape_html(thinking.thinking)
|
|
249
229
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
230
|
+
return f"""
|
|
231
|
+
<div class="tool-content thinking-content">
|
|
232
|
+
<details>
|
|
233
|
+
<summary><strong>💭 Thinking</strong></summary>
|
|
234
|
+
<div class="thinking-text">
|
|
235
|
+
<pre>{escaped_thinking}</pre>
|
|
236
|
+
</div>
|
|
237
|
+
</details>
|
|
238
|
+
</div>
|
|
239
|
+
"""
|
|
254
240
|
|
|
255
241
|
|
|
256
242
|
def format_image_content(image: ImageContent) -> str:
|
|
@@ -618,11 +604,9 @@ def generate_html(messages: List[TranscriptEntry], title: Optional[str] = None)
|
|
|
618
604
|
if command_args:
|
|
619
605
|
content_parts.append(f"<strong>Args:</strong> {escaped_command_args}")
|
|
620
606
|
if command_contents:
|
|
621
|
-
|
|
622
|
-
f"<div class='content'>{escaped_command_contents}</div>"
|
|
607
|
+
content_parts.append(
|
|
608
|
+
f"<details><summary>Content</summary><div class='content'>{escaped_command_contents}</div></details>"
|
|
623
609
|
)
|
|
624
|
-
details_html = create_collapsible_details("Content", details_content)
|
|
625
|
-
content_parts.append(details_html)
|
|
626
610
|
|
|
627
611
|
content_html = "<br>".join(content_parts)
|
|
628
612
|
message_type = "system"
|
|
@@ -60,86 +60,6 @@
|
|
|
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
|
-
|
|
143
63
|
.tool-content {
|
|
144
64
|
background-color: #f8f9fa66;
|
|
145
65
|
border-radius: 4px;
|