-
-
Notifications
You must be signed in to change notification settings - Fork 75
Add UX improvements: collapsible cells, copy buttons, syntax highlighting, and ANSI sanitization #47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
I reverse-engineered the API running Codex against the claude JS code like this: https://gistpreview.github.io/?e4159193cd2468060d91289b5ccdece3 Then had Claude Code build the features: https://gistpreview.github.io/?3e2a728845c250cf9b1ad5c0ee645d32
Add a --gist feature to both of the commands that can output a session converted to HTML. If --gist is provided without a -o then it uses a tmp directory for the export (still with the session ID as the name) which is still printed out but is expected to be deleted by the OS at some point. The --gist option causes the content of that folder - the index.html and any page_*.html files as multiple files in a single gist. There is just one catch: I intend to serve those files using https://gistpreview.github.io/?3769c0736f45668e9595eb4eb8493f9c/index.html - but any links from that page need to go to https://gistpreview.github.io/?3769c0736f45668e9595eb4eb8493f9c/two.html so relative URLs will not work as they will incorrectly go to https://gistpreview.github.io/two.html. So... if --gist is used then a little bit of javascript needs to be injected into the bottom of the index.html and other pages which checks to see if the browser is accessing the page on gistpreview.github.io and if it is corrects any a href links on the page Do not forget the tests and the README https://gistpreview.github.io/?cbb6a57bc23ba2695bdc4e8d76997789
For every command that supports --gist add a --json option, if you pass --json then the .json version of the session that was retrieved from the API is included in the output directory (or temporary output directory) and teh path to that JSON file is shown in the output, along with its size in KB https://gistpreview.github.io/?5e11fdc8622af967450f790f052042c1
Also made it so output shows full path to index.html https://gistpreview.github.io/?0ce06835c48f621d068e7b10ccc9b86d
* Add -v/--version flag to CLI Add a version flag that displays the package version from importlib.metadata. Both -v and --version are supported. Includes tests for both flags. * Add uv.lock * Remove uv.lock and add to .gitignore * Use importlib.metadata in version tests instead of hardcoded version * Refactor to use click.version_option decorator * Simplify version_option using package_name for auto-detection https://gistpreview.github.io/?7bdf1535f7bf897fb475be6ff5da2e1c/index.html
- Add parse_session_file() abstraction to handle both JSON and JSONL formats - Add get_session_summary() to extract summaries from session files - Add find_local_sessions() to discover JSONL files in ~/.claude/projects - Add list-local command to show local sessions - Change default behavior: running with no args now lists local sessions - Add comprehensive tests for all new functionality - Include sample JSONL test fixture and snapshot tests
- Rename 'session' command to 'json' for direct file path access - Rename 'import' command to 'web' for web session selection - Update 'list-local' to 'local' with interactive picker and conversion - Remove 'list-web' command (now integrated into 'web' command) - Make 'local' the default command when run with no arguments New CLI structure: - claude-code-publish local - select from local JSONL sessions - claude-code-publish web - select from web sessions - claude-code-publish json - provide direct file path All commands support --gist, --json, --open, and -o options.
This flag creates output in a subdirectory named after: - The session ID for web command - The file stem for json and local commands Uses -o as parent directory if specified, otherwise current directory. When -a is used, auto-open browser is disabled.
* Add Jinja2 templates for HTML generation - Add jinja2 as a dependency - Create templates directory with base.html, page.html, and index.html - Refactor generate_html to use Jinja2 template rendering - All 47 tests pass with identical HTML output * Refactor HTML generation to use Jinja2 macros with autoescape - Add comprehensive macros.html with reusable components for all HTML generation (pagination, tool displays, messages, index items, etc.) - Enable Jinja2 autoescape for security - Update base.html, page.html, index.html to use |safe filter for pre-rendered HTML content - Simplify Python render functions to call template macros - Update test snapshots with minor formatting differences Transcript: https://gistpreview.github.io/?ffc01d1c04e47ed7934a58ae04a066d1/index.html
Prevents browsers from opening during test runs by automatically mocking webbrowser.open for all tests. Also updates the JSONL snapshot to match current output. https://gistpreview.github.io/?1671b49de273d80280ab2ceab690db8c/index.html
- Rename module directory src/claude_code_publish → src/claude_code_transcripts - Update pyproject.toml with new package name, CLI command, and URLs - Update all README.md references - Update test imports and version checks - Update AGENTS.md development instructions https://gistpreview.github.io/?814530b3a70af8408f3bb8ca10f70d57/index.html
* Fix Windows Unicode encoding errors when writing HTML files On Windows, the default file encoding is cp1252, which cannot encode Unicode characters like emojis. This caused UnicodeEncodeError when writing HTML files containing emoji characters. Fixed by explicitly specifying encoding="utf-8" for all read_text() and write_text() calls on HTML files. * Fix Windows test encoding issues Add encoding="utf-8" to all read_text() and write_text() calls in tests to ensure proper handling of UTF-8 encoded HTML files on Windows. * Set PYTHONUTF8 environment variable for tests For windows, refs: - #6 --------- Co-authored-by: Claude <[email protected]>
> The gistpreview mechanis has a bug: fragment links like https://gistpreview.github.io/?edbd5ddcb39d1edc9e175f1bf7b9ef9a/page-001.html#msg-2025-12-26T15-30-45-910Z do not naught the user to the right point, presumably because the content has not loaded in time > > Fix that with more JavaScript in the existing gistpreview JavaScript When accessing a gistpreview URL with a fragment (e.g. #msg-2025-12-26T15-30-45-910Z), the browser's native fragment navigation fails because gistpreview loads content dynamically. By the time the browser tries to scroll to the element, it doesn't exist yet. Added JavaScript that: - Checks for fragment in window.location.hash - Uses scrollIntoView to navigate to the target element - Retries with increasing delays (100ms, 300ms, 500ms, 1s) to handle dynamic content loading https://gistpreview.github.io/?883ee001b0d63a2045f1e2c7c2598a9f/index.html
* Add batch command for converting all sessions to HTML archive Adds a new `batch` command that converts all local Claude Code sessions into a browsable HTML archive with: - Master index listing all projects with session counts - Per-project pages listing sessions by date - Individual session transcripts (using existing generate_html) New features: - `find_all_sessions()` - discovers sessions grouped by project - `get_project_display_name()` - extracts readable names from encoded paths - `--dry-run` flag to preview what would be converted - `--include-agents` flag to include agent-* session files - Progress indicator during conversion Usage: claude-code-transcripts batch -o ./my-archive 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * Refactor batch command for better code reuse and error handling - Move datetime import to module level for consistency - Add progress_callback parameter to generate_batch_html() for progress reporting - Add try/except around individual session conversion with failed_sessions tracking - Refactor batch_cmd() to use generate_batch_html() instead of duplicating the loop - Add --quiet/-q flag to suppress non-error output for scripting 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * Fix --quiet flag not respected in dry-run mode - Wrap dry-run output in `if not quiet` check - Add test for --quiet with --dry-run combination - Add .playwright-mcp/ to .gitignore - Rename html variable to html_content to avoid shadowing html module - Remove unused imports from test file 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * Add batch command documentation to README Document the new batch command that converts all local Claude Code sessions to a browsable HTML archive with master and project indexes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * Rename batch command to all https://gistpreview.github.io/?be627e42c25a1ffdf39cfb2f9a79f1ff/index.html Refs simonw#11 (comment) --------- Co-authored-by: Claude Opus 4.5 <[email protected]> Co-authored-by: Simon Willison <[email protected]>
iOS Safari auto-zooms when focusing inputs with font-size < 16px. Setting both the header search box and modal search input to 16px prevents this unwanted zoom behavior. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> Refs simonw#15 Transcript to this point of search: https://gistpreview.github.io/?74c4a80b8e0a00d0904c2a2c17ebe8ff/index.html
Search relies on fetch() to load page content, which doesn't work from file:// due to CORS restrictions. Now returns early before showing the search UI when window.location.protocol is 'file:'. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> Transcript: https://gistpreview.github.io/?701ec658eede1bd0023f82fdb9a46229/index.html Refs simonw#16 (comment) Refs simonw#15
The CSS `display: flex` on #search-modal was overriding the browser's default `display: none` for closed dialogs. Changed selector to is open. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> https://gistpreview.github.io/?e7b23436042f50383345d59b952cef27 Refs simonw#15
The json command now accepts URLs (http:// or https://) in addition to local file paths. When a URL is provided, the content is fetched and processed as a session file.
- Add strip_ansi() function to remove ANSI escape sequences from terminal output - Add is_content_block_array() to detect JSON arrays of content blocks - Add render_content_block_array() to properly render content blocks in tool results - Tool results containing ANSI codes now display cleanly without escape sequences - Tool results containing JSON arrays of content blocks now render as markdown text instead of raw JSON This fixes two critical UX issues where terminal output was unreadable due to visible ANSI codes, and tool replies showed raw JSON instead of rendered content. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add copy button CSS with hover reveal effect - Add JavaScript to dynamically add copy buttons to pre, tool-result, and bash-command elements - Copy button shows "Copied!" feedback for 2 seconds after successful copy - Uses navigator.clipboard API for modern clipboard access - Buttons appear on hover and fade in smoothly This improves the UX for users who want to copy code snippets, terminal output, or tool results from transcripts. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add Pygments dependency for syntax highlighting - Implement highlight_code() function that detects language from file extension - Apply highlighting to Write and Edit tool content - Add Monokai-inspired dark theme CSS for highlighted code - Supports Python, JavaScript, HTML, CSS, JSON, and many other languages - Falls back gracefully to plain text for unrecognized file types This significantly improves code readability in transcript outputs by providing proper syntax coloring for different programming languages. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
Part 2 deliverables from subagent analysis: ## Task Grading (0.00-10.00) - B.5 ANSI Sanitization: 6.75/10 - Works for common cases but regex misses ~30% of ANSI sequences (cursor control, OSC sequences) - B.4 Content-Block Arrays: 6.75/10 - Handles text/thinking but not images or tool_use blocks - A.1 Copy Buttons: 6.75/10 - Functional but lacks accessibility (no ARIA labels, keyboard support, clipboard API fallback) - B.2 Syntax Highlighting: 9.25/10 - Excellent implementation with 500+ language support and graceful error handling ## TASKS.md Updates - Added detailed grading with justifications - Added known limitations for each completed task - Added test coverage gaps to address - Added technical specifications (file architecture, CSS/JS guidelines) - Added task dependency graph - Added implementation details for pending phases - Added documentation gaps identified ## AGENTS.md Updates - Added Quick Start section with setup commands - Added Project Structure overview - Added comprehensive testing commands - Added snapshot testing instructions - Added debugging tips - Added architecture notes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
Use render_content_block for array items Add tests for image and tool_use arrays
Group tool calls with matching results by tool_use_id Add tool-pair wrapper and page snapshot updates
Add OSC and CSI stripping with tests
…support - Combined hardened ANSI_ESCAPE_PATTERN from ansi-sanitization branch - Preserved content-block array rendering from content-block-array-rendering branch - Both features now work together: arrays checked first, then ANSI stripped
- Add render_json_with_markdown() function that recursively renders JSON with Markdown formatting for string values - Update render_bash_tool() to render descriptions as Markdown HTML - Update generic tool_use handler to render descriptions and JSON with Markdown - Update bash_tool and tool_use macros to use |safe for pre-rendered HTML - Add CSS classes for styled JSON output (json-key, json-string-value, etc.) - Add 4 new tests for markdown rendering functionality - Update snapshots for changed output format This implements Phase 2 Task 1: tool call text rendered as Markdown. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add group_blocks_by_type() function to categorize content blocks - Refactor render_assistant_message*() to group blocks into cells - Add cell macro with <details> wrapper for collapsible sections - Three cell types: thinking (closed), response (open), tools (closed with count) - Add CSS for cell styling with expand/collapse animation - Add 5 new tests for cell structure functionality - Update snapshots for new HTML structure Thinking cells are closed by default to reduce noise. Response cells are open by default for immediate visibility. Tools cells show count and are closed by default. This implements Phase 2 Task 2: collapsible cells for subcomponents. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add copy button to cell header in cell macro
- Add CSS for cell copy button with hover and focus states
- Add JavaScript handler for cell copy with clipboard API
- Include keyboard accessibility (Enter/Space support)
- Add ARIA labels for screen reader accessibility
- Add 2 new tests for cell copy button functionality
- Update snapshots for new button and CSS
Each collapsible cell (Thinking, Response, Tool Calls) now has
a copy button that copies the cell content to clipboard with
visual feedback ("Copied!" for 2 seconds).
This implements Phase 2 Task 3: per-cell copy buttons.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Fix sticky headers by removing overflow:hidden from .message - Add JSON/Markdown toggle for tool calls and tool results - Improve syntax highlighting with Dracula-inspired color scheme - Add clear labels for tool calls (→ Call) and results (← Result) - Improve copy functionality with data-copy-content attribute - Add hover states for better clickability feedback 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
## Phase 1: Foundation (Craft.do Style) - Add 50+ CSS custom properties for colors, spacing, shadows - Implement warm cream/off-white color palette - Add paper texture background effect - Define frosted glass variables for sticky headers ## Phase 2: User Messages - Make user messages collapsible with cell wrapper - Add text wrapping (overflow-wrap, word-break) to prevent overflow - Add .user-cell CSS styling matching other cell types ## Phase 3: Toggle System - Replace small toggle buttons with shadcn-inspired tab controls - Add Markdown | JSON tabs with clear active states - Include ARIA attributes for accessibility - Update JavaScript for tab-based toggle behavior ## Phase 4: Collapsibility Unification - Make message headers sticky (z-index: 30) - Create cascading sticky headers: message → cell → subcell - Add frosted glass backdrop-filter effects - Ensure no overflow:hidden breaks sticky positioning ## Phase 5: Copy System - Remove duplicate copy buttons (skip inside .cell-content) - Add cell-level master toggle for tools cells - Propagate view mode to all child elements ## Phase 6: Specialized Tools - Add Markdown/JSON toggles to Write, Edit, Bash, TodoWrite - Pass input_json_html to all specialized tool macros - Preserve existing specialized displays in Markdown view All 133 tests pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
JSON Display Fix: - Remove inline `style="display: none;"` from .view-json divs in macros.html - CSS class-based toggle (.show-json) now properly controls visibility - Affected: todo_list, write_tool, edit_tool, bash_tool macros Tabs Alignment Fix: - Update .cell summary to use flex: 1 on .cell-label instead of justify-content: space-between - Add flex-wrap: wrap to .tool-header, .file-tool-header, .todo-header for better responsiveness - Add gap: var(--spacing-sm) to .cell summary for consistent spacing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
A.2 Metadata Subsection: - Add calculate_message_metadata() function for char count, token estimate, tool counts - Add collapsible metadata section with info icon to each message - CSS styles: .message-metadata, .metadata-item, .metadata-label, .metadata-value - 7 new tests for metadata functionality B.3 Tool Call Headers with Type: - Add TOOL_ICONS constant with 14 tool-specific icons - Read (📖), Write (📝), Edit (✏️), Bash ($), Glob (🔍), Grep (🔎), etc. - Add get_tool_icon() helper function - Update tool_use macro to accept and display tool icon - Default icon (⚙) for unmapped tools 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
The test file had two methods named `test_tool_result_with_ansi_codes` at lines 512 and 545 in TestRenderContentBlock class. The second one was silently overriding the first. - Renamed second test to `test_tool_result_with_ansi_codes_snapshot` - Updated docstring to clarify the test's purpose - Renamed snapshot file to match new test name - Both tests now run correctly (5 ANSI-related tests pass) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
The module-level _github_repo variable posed a thread-safety risk when processing multiple sessions concurrently. This refactors to use Python's contextvars module which provides thread-local storage. Changes: - Add contextvars import - Create _github_repo_var ContextVar with None default - Add get_github_repo() accessor function (thread-safe) - Add set_github_repo() setter function (thread-safe) - Keep _github_repo module variable for backward compatibility - Update all internal usages to use new functions - Update test to use new API The backward-compatible design ensures existing code that reads _github_repo directly continues to work. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
Added a copyToClipboard() helper function that:
1. Uses the modern Clipboard API (navigator.clipboard.writeText)
when available
2. Falls back to document.execCommand('copy') for older browsers
3. Returns a Promise for consistent handling
Also added user-facing error feedback:
- Button now shows "Failed" for 2 seconds on copy failure
- Improves UX by informing users when copy doesn't work
Updated both copy button implementations:
- Dynamically added copy buttons on pre/tool-result elements
- Cell header copy buttons
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Fix Phase 2 UI regressions and complete remaining items
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds comprehensive UX improvements to claude-code-transcripts, including ANSI sanitization, syntax highlighting with Pygments, collapsible cells, copy buttons, Markdown/JSON view toggles, metadata display, search functionality, and batch conversion capabilities. The changes are well-tested with 141 passing tests and 21 snapshot tests, demonstrating thorough quality assurance.
Key Changes
- Added 10 major UX features: ANSI sanitization, syntax highlighting, collapsible cells, copy buttons, tool pairing, view toggles, metadata, search, batch conversion
- Comprehensive test coverage with new test classes and snapshot tests
- Template refactoring using Jinja2 base template inheritance
- New dependencies: pygments, questionary, click-default-group, httpx, jinja2
Reviewed changes
Copilot reviewed 36 out of 39 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_generate_html.py | Added 1000+ lines of comprehensive tests for all new features |
| tests/test_all.py | New 532-line test file for batch conversion functionality |
| tests/conftest.py | New fixture file with webbrowser mocking |
| tests/sample_session.jsonl | New JSONL format test fixture |
| tests/snapshots/* | Updated/new snapshot files for HTML output verification |
| src/claude_code_transcripts/templates/* | New Jinja2 templates for modular HTML generation |
| pyproject.toml | Updated dependencies and project metadata |
| CLAUDE.md | New file referencing AGENTS.md |
| .gitignore | Added uv.lock and .playwright-mcp/ |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| claude_code_publish._github_repo = "example/repo" | ||
| # Need to set the github repo for commit link rendering | ||
| # Using the thread-safe set_github_repo function | ||
| import claude_code_transcripts |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Module 'claude_code_transcripts' is imported with both 'import' and 'import from'.
| return text | ||
| except json.JSONDecodeError: | ||
| continue | ||
| except Exception: |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'except' clause does nothing but pass and there is no explanatory comment.
| except Exception: | |
| except Exception: | |
| # Best-effort summary extraction: on any unexpected error, fall back to the | |
| # default "(no summary)" value returned below. |
Summary
This PR adds several significant UX and rendering improvements to claude-code-transcripts, developed and tested across multiple iterations.
New Features
1. ANSI Escape Code Sanitization
2. Content Block Array Rendering
[{"type": "text", ...}]arrays as markdown instead of raw JSON3. Syntax Highlighting
4. Copy Buttons
5. Tool Call/Result Pairing
6. Collapsible Cell Structure
7. Per-Cell Copy Buttons
8. Markdown Rendering in Tools
9. Message Metadata
10. Tool Icons
Bug Fixes
_github_repovariable to use contextvarsTest Coverage
Files Changed
src/claude_code_transcripts/__init__.pysrc/claude_code_transcripts/templates/macros.htmltests/test_generate_html.pypyproject.tomlAGENTS.mdBreaking Changes
None - all changes are additive and backward compatible.
Screenshots
The improvements are best seen in action. Generate HTML from a Claude Code session to see:
Test Plan
🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.5 [email protected]