Custom marked renderer wraps fenced code blocks in a .code-block-wrapper
div containing a language label badge (top-right) and a copy button.
The language label shows the fenced language identifier in uppercase.
A delegated click handler on the document root intercepts clicks on
[data-copy-code] buttons, reads the sibling <code> element's text
content, writes it to the clipboard, and shows a "Copied!" / "Failed"
confirmation that auto-reverts after 1.5 seconds. Delegated handling
is necessary because code blocks are rendered via dangerouslySetInnerHTML
and don't participate in React's synthetic event system.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Performance: Replace hljs.highlightAuto() fallback with plain escapeHtml()
for unlabeled code blocks. highlightAuto tries every grammar (~6.7ms/block)
while escapeHtml costs ~0.04ms. With thousands of unlabeled blocks in
typical sessions this dominated render time.
Import shared escapeHtml instead of the local duplicate. Import github-dark
highlight.js theme CSS directly.
Fix highlightSearchText to avoid corrupting HTML entities: split text on
entity patterns (& < etc.) before applying search regex, so searching
for 'amp' does not break & into &<mark>amp</mark>;.
Add unit tests for highlightSearchText covering: plain text matches, empty
queries, avoiding matches inside HTML tags, and preserving HTML entities.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Establish a cohesive dark UI foundation:
- Define CSS custom properties for surfaces (4-level depth hierarchy),
borders, foreground text (3-tier), accent colors, and canvas background
- Add Inter (text) and JetBrains Mono (code) font loading via Google Fonts
- Extend Tailwind with semantic color tokens, typography scale (caption/body/
subheading/heading), box shadows (card, glow), and animations (fade-in,
slide-in, skeleton shimmer)
- Add component-layer utilities: .btn system (primary/secondary/ghost/danger),
.glass frosted overlays, .custom-checkbox, .skeleton loaders
- Add .prose-message typographic styles for rendered markdown content
- Add search minimap CSS (tick marks, viewport indicator)
- Restyle scrollbars for thin dark appearance (WebKit + Firefox)
- Replace hardcoded Tailwind color classes in CATEGORY_COLORS with semantic
tokens (dot/border/text) mapped to the new design system
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Full React 18 client application for interactive session browsing:
app.tsx:
- Root component orchestrating session list, viewer, filters, search,
redaction controls, and export — wires together useSession and
useFilters hooks
- Keyboard navigation: j/k or arrow keys for message focus, Escape to
clear search and redaction selection, "/" to focus search input
- Derives filtered messages, match count, visible UUIDs, and category
counts via useMemo to avoid render-time side effects
hooks/useSession.ts:
- Manages session list and detail fetching state (loading, error,
data) with useCallback-wrapped fetch functions
- Auto-loads session list on mount
hooks/useFilters.ts:
- Category filter state with toggle, set-all, and preset support
- Text search with debounced query propagation
- Manual redaction workflow: select messages, confirm to move to
redacted set, undo individual or all redactions, select-all-visible
- Auto-redact toggle for the sensitive-redactor module
- Returns memoized object to prevent unnecessary re-renders
components/SessionList.tsx:
- Two-phase navigation: project list → session list within a project
- Groups sessions by project, shows session count and latest modified
date per project, auto-drills into the correct project when a session
is selected externally
- Formats project directory names back to paths (leading dash → /)
components/SessionViewer.tsx:
- Renders filtered messages with redacted dividers inserted where
manually redacted messages were removed from the visible sequence
- Loading spinner, empty state for no session / no filter matches
- Scrolls focused message into view via ref
components/MessageBubble.tsx:
- Renders individual messages with category-specific Tailwind border
and background colors
- Markdown rendering via marked + highlight.js, with search term
highlighting that splits HTML tags to avoid corrupting attributes
- Click-to-select for manual redaction, visual selection indicator
- Auto-redact mode applies sensitive-redactor to content before render
- dangerouslySetInnerHTML is safe here: content is from local
user-owned JSONL files, not untrusted external input
components/FilterPanel.tsx:
- Checkbox list for all 9 message categories with auto-redact toggle
components/SearchBar.tsx:
- Debounced text input (200ms) with match count display
- "/" keyboard shortcut to focus, × button to clear
components/ExportButton.tsx:
- POSTs current session + visible/redacted UUIDs + auto-redact flag
to /api/export, downloads the returned HTML blob as a file
components/RedactedDivider.tsx:
- Dashed-line visual separator indicating redacted content gap
lib/types.ts:
- Re-exports shared types via @shared path alias for client imports
lib/constants.ts:
- Tailwind CSS class mappings per message category (border + bg colors)
lib/markdown.ts:
- Configured marked + highlight.js instance with search highlighting
that operates on text segments only (preserves HTML tags intact)
styles/main.css:
- Tailwind base/components/utilities, custom scrollbar, highlight.js
overrides, search highlight mark, redaction selection outline,
message dimming for non-matching search results
index.html + main.tsx:
- Vite entry point mounting React app into #root with StrictMode
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>