Commit Graph

28 Commits

Author SHA1 Message Date
teernisse
007cbbcb69 Enhance UI with density toggle and mobile responsiveness
Add density toggle, mobile sidebar behavior, and various polish improvements
to the session viewer interface.

Density toggle (comfortable/compact):
- Persisted to localStorage as "session-viewer-density"
- Compact mode reduces message padding and spacing
- Toggle button in nav bar with LayoutRows icon
- Visual indicator when compact mode is active

Mobile sidebar:
- Sidebar slides in/out with transform transition
- Hamburger menu button visible on mobile (md:hidden)
- Backdrop overlay when sidebar is open
- Auto-close sidebar after selecting a session

Session info improvements:
- Show project/session title in nav bar when viewing session
- Add session ID display with copy button in SessionViewer
- Copy button shows check icon for 1.5s after copying

SessionList enhancements:
- Relative time format: "5m ago", "2h ago", "3d ago"
- Total message count per project in project list
- Truncated project names showing last 2 path segments
- Full path available in title attribute

MessageBubble:
- Add compact prop for reduced padding
- Add accentBorder property to category colors
- Migrate inline SVGs to shared Icons

SessionViewer:
- Sticky session info header with glass effect
- Add compact prop that propagates to MessageBubble
- Keyboard shortcut hints in empty state

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-28 00:53:43 -05:00
teernisse
b67c302464 Add Icons component and migrate inline SVGs
Extract reusable icon components to a new Icons.tsx module, reducing
code duplication and enabling consistent icon styling across the app.

Icons included:
- Navigation: ChevronRight, ChevronLeft, ChevronDown, ChevronUp
- Actions: Search, X, Copy, Check, Refresh, Download
- UI: Menu, LayoutRows, Filter, Chat, ChatBubble
- Status: EyeSlash, Shield, AlertCircle, Clipboard
- Loading: Spinner (uses fill instead of stroke)

Implementation:
- icon() factory function creates SVG components from path data
- Configurable size (default "w-4 h-4") and strokeWidth (default 1.5)
- All icons use currentColor for easy theming

Components migrated:
- MessageBubble: collapse chevron, copy button
- SessionList: back arrow, project chevron, chat bubble
- SessionViewer: chat icon, filter icon

Also adds keyboard shortcut hints to the empty session state,
helping users discover j/k navigation and / search commands.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-28 00:53:30 -05:00
3fe8d7d3b5 Add comprehensive test suite for progress tracking system
Test fixture updates:
- Add toolUseId fields (toolu_read1, toolu_edit1) to tool_use blocks
- Add parentToolUseID-linked progress events for read and edit tools
- Add orphaned SessionStart progress event (no parent)
- Update tool_result references to match new toolUseId values
- Add bash_progress and mcp_progress subtypes for subtype derivation

session-parser tests (7 new):
- toolUseId extraction from tool_use blocks with and without id field
- parentToolUseId and progressSubtype extraction from hook_progress
- Subtype derivation for bash_progress, mcp_progress, agent_progress
- Fallback to "hook" for unknown data types
- Undefined parentToolUseId when field is absent

progress-grouper tests (7 new):
- Partition parented progress into toolProgress map
- Remove parented progress from filtered messages array
- Keep orphaned progress (no parentToolUseId) in main stream
- Keep progress with invalid parentToolUseId (no matching tool_call)
- Empty input handling
- Sort each group by rawIndex
- Multiple tool_call parents tracked independently

agent-progress-parser tests (full suite):
- Parse user text events with prompt/agentId metadata extraction
- Parse tool_use blocks into AgentToolCall events
- Parse tool_result blocks with content extraction
- Parse text content as text_response with line counting
- Handle multiple content blocks in single turn
- Post-pass tool_result→tool_call linking (sourceTool, language)
- Empty input and malformed JSON → raw_content fallback
- stripLineNumbers for cat-n prefixed output
- summarizeToolCall for Read, Grep, Glob, Bash, Task, WarpGrep, etc.

ProgressBadge component tests:
- Collapsed state shows pill counts, hides content
- Expanded state shows all event content via markdown
- Subtype counting accuracy
- Agent-only events route to AgentProgressView

AgentProgressView component tests:
- Prompt banner rendering with truncation
- Agent ID and turn count display
- Summary rows with timestamps and tool names
- Click-to-expand drill-down content

html-exporter tests (8 new):
- Collapsible rendering for thinking, tool_call, tool_result
- Toggle button and JavaScript inclusion
- Non-collapsible messages lack collapse attributes
- Diff content detection and highlighting
- Progress badge rendering with toolProgress data

filters tests (2 new):
- hook_progress included/excluded by category toggle

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 23:05:01 -05:00
d4de363227 Polish UI: SearchBar sizing, SessionList layout, Tooltip offset, and progress CSS
SearchBar:
- Switch from fixed w-80/sm:w-96 to fluid min-w-80 max-w-md w-full
- Adjust left padding for better visual alignment with search icon

SessionList:
- Memoize project grouping computation with useMemo
- Move horizontal padding from per-item margin (mx-2 + width calc hack)
  to container padding (px-2) for cleaner layout and full-width buttons
- Remove inline width override that was compensating for the old margins

Tooltip:
- Increase offset from 8px to 12px for better visual separation

CSS:
- Add prose-message-progress variant with compact 11px mono typography
  for progress event content (code blocks, tables, links, blockquotes)
- Reduce search minimap marker height from 4px to 3px
- Normalize prose-message line-height: paragraphs 1.625→1.6, list
  items 1.5→1.6 for consistent rhythm
- Switch custom checkbox checkmark sizing from fixed px to percentages
  for better scaling across different zoom levels

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 23:04:38 -05:00
f69ba1f32a Wire progress events through session viewer and fix request race condition
MessageBubble:
- Accept progressEvents and progressEnabled props
- Render ProgressBadge below tool_call content when progress data exists
- Normalize left padding from mixed pl-5/px-4 to consistent px-5

SessionViewer:
- Thread toolProgress map and progressEnabled flag through to messages
- Look up progress events by toolUseId for each tool_call message
- Fix hash-anchor scroll firing on every filter toggle by tracking
  whether scroll has occurred per session load (hashScrolledRef)
- Increase vertical spacing on time gap dividers and header area

App:
- Derive progressEnabled from hook_progress category filter state
- Pass toolProgress and progressEnabled to SessionViewer
- Optimize sensitiveCount to compute across all session messages
  (not filtered subset) to avoid re-running 37 regex patterns on
  every filter toggle
- Tighten redaction selection badge gap from 2 to 1.5

useSession:
- Add AbortController to loadSession to cancel stale in-flight requests
  when user rapidly switches sessions
- Only clear loading state if the completing request is still current
- Ignore AbortError exceptions from cancelled fetches

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 23:04:04 -05:00
9c4fc89cac Add progress visualization components: ProgressBadge, ProgressGroup, AgentProgressView
Three new components for rendering tool progress events inline:

ProgressBadge — Attaches below tool_call messages. Shows color-coded
pills counting events by subtype (hook/bash/mcp/agent). Expands on
click to show either a timestamped event log (mixed subtypes) or the
full AgentProgressView (all-agent subtypes). Lazy-renders markdown
only when expanded via useMemo.

ProgressGroup — Standalone progress divider for orphaned progress
events in the message stream. Centered pill-style summary with event
count, subtype breakdown, and time range. Expands to show the same
timestamped log format as ProgressBadge.

AgentProgressView — Rich drill-down view for agent sub-conversations.
Parses agent_progress JSON via parseAgentEvents into a structured
activity feed with:
- Header showing quoted prompt, agent ID, turn count, and time range
- Per-event rows with timestamp, SVG icon (10 tool-specific icons),
  short tool name, and summarized description
- Click-to-expand drill-down rendering tool inputs as JSON code blocks,
  tool results with language-detected syntax highlighting (after
  stripping cat-n line numbers), and text responses as markdown

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 23:03:46 -05:00
d7246cf062 Add client-side agent progress parser with tool call summarization
New agent-progress-parser.ts provides rich parsing of agent_progress
JSON payloads from hook_progress events into a structured event stream:

Event types: AgentToolCall, AgentToolResult, AgentTextResponse,
AgentUserText, AgentRawContent — unified under AgentEvent discriminated
union with ParsedAgentProgress metadata (prompt, agentId, timestamps,
turnCount).

Key capabilities:
- Parse nested message.message.content blocks from agent progress JSON
- Post-pass linking of tool_results to their preceding tool_calls via
  toolUseId map, enriching results with sourceTool and language hints
- Language detection from file paths (30+ extensions mapped)
- stripLineNumbers() to remove cat-n style prefixes for syntax detection
- summarizeToolCall() for human-readable one-line summaries of common
  Claude tools (Read, Write, Edit, Grep, Glob, Bash, Task, WarpGrep)

Also re-exports ProgressSubtype from client types barrel.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 23:03:29 -05:00
150cd0c686 Wire up ErrorBoundary, URL session sync, j/k navigation, and refresh
Wrap SessionViewer in an ErrorBoundary so render errors show a
recovery UI instead of a white screen.

Sync the selected session ID with the URL search param (?session=).
On initial load, load the session from the URL if it exists in the
session list. On session change, update the URL via
history.replaceState without triggering navigation.

Add j/k keyboard navigation to step through filtered messages.
Search focus takes precedence over keyboard focus; both reset when
filters or session changes.

Add a refresh button in the sidebar header that calls the new
refreshSessions hook, with a spinning icon while loading.

Pass sensitiveCount to FilterPanel for the new badge display.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 13:36:18 -05:00
4ec186d45b Skip entrance animation for messages beyond the first 20
Messages at index 20+ no longer receive the animate-fade-in class or
animationDelay inline style. This avoids scheduling hundreds of CSS
animations on large sessions where the stagger would be invisible
anyway (the earlier cap of 300ms max delay was already clamping them).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 13:36:02 -05:00
957f9bc744 Polish SearchBar with arrow key navigation and conditional controls
Arrow Up/Down keys now cycle through search matches while the input
is focused, matching the behavior of browser find-in-page. Navigation
buttons gain active:scale-95 press feedback and explicit sizing.

The right-side control region is now conditionally rendered: keyboard
hint (/) shows when empty, match count + nav + clear show when active.
A visual divider separates navigation arrows from the clear button.
The match count badge highlights the current position number with a
distinct weight.

Tests cover empty state visibility, active search state rendering,
arrow key and button navigation, and clear button behavior.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 13:35:55 -05:00
10f23ccecc Pretty-print JSON in tool inputs and preformatted blocks
Tool call inputs and structured data categories (hook_progress,
file_snapshot) now attempt JSON.parse + JSON.stringify(_, null, 2)
before escaping to HTML. Non-JSON content passes through unchanged.
The detection fast-paths by checking the first non-whitespace
character for { or [ before attempting parse.

Also renames the copy state variable from linkCopied to contentCopied
to match the current behavior of copying message content rather than
anchor links.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 13:35:45 -05:00
b0b330e0ba Add session list refresh with server cache bypass
useSession now exposes a refreshSessions() callback that fetches
/api/sessions?refresh=1. The sessions route checks for the refresh
query parameter and resets the cache timestamp to zero, forcing a
fresh scan of ~/.claude/projects/ on the next request.

This enables users to pick up new sessions without restarting the
server or waiting for the 30-second cache to expire.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 13:35:37 -05:00
e5c5e470b0 Add ErrorBoundary component with recovery UI
React class component that catches render errors in child trees and
displays a styled error panel with the exception message and a
'Try again' button that resets state. Follows the existing design
system with red accent colors and gradient icon background.

Tests verify normal rendering, error capture with message display,
and the presence of the recovery button.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 13:35:30 -05:00
89ee0cb313 Add Tooltip component and show sensitive message count on auto-redact
Introduce a reusable Tooltip component with delayed hover reveal,
viewport-aware horizontal nudging, and smooth CSS entrance animation.
Supports top/bottom positioning via a data-side attribute.

FilterPanel now wraps the auto-redact checkbox in a Tooltip that
explains what auto-redaction detects. When sensitive messages exist
in the current view, a red pill badge displays the count next to
the label, giving users immediate visibility into how many messages
contain detectable secrets before toggling auto-redact on.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 13:35:23 -05:00
4027dd65be Persist filter and auto-redact preferences to localStorage
Category toggles and the auto-redact checkbox now survive page
reloads. On mount, useFilters reads from localStorage keys
session-viewer:enabledCategories and session-viewer:autoRedact,
falling back to defaults when storage is empty, corrupted, or
contains invalid category names. Each state change writes back
to localStorage in a useEffect.

Tests cover round-trip persistence, invalid data recovery, corrupted
JSON fallback, and the boolean coercion for auto-redact.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 13:35:08 -05:00
c66ce4ae16 Change message copy button to copy content instead of anchor link
The per-message copy button previously copied a URL with a hash
anchor (#msg-{uuid}) for deep linking. Replace this with copying
the actual message content: for tool_call messages it copies the
tool name and input; for all other categories it copies the text
body. This is more immediately useful — users copying message
content is far more common than sharing anchor links.

Button title updated from "Copy link to message" to "Copy message
content" to match the new behavior.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 10:42:30 -05:00
a51c134da7 Harden API layer: encode session IDs and validate export payload
Session fetch (useSession.ts):
- Wrap the session ID in encodeURIComponent before interpolating
  into the fetch URL. Session IDs can contain characters like '+'
  or '/' that would corrupt the path without encoding.

Export route (export.ts):
- Add validation that redactedMessageUuids, when present, is an
  array. Previously only visibleMessageUuids was checked, so a
  malformed redactedMessageUuids value (e.g. a string or object)
  would silently pass validation and potentially cause downstream
  type errors in the HTML exporter.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 09:34:29 -05:00
4c5d6dd4c8 Extend search to match tool input content, not just message body
The search index (app.tsx) and the per-message match check
(SessionViewer.tsx) now also search msg.toolInput when present.
This means searching for a file path, command, or argument that
appears in a tool call's input will correctly highlight and
navigate to that message, rather than dimming it as a non-match.

Both locations use the same compound condition so the match index
and the visual dimming/focus logic stay in sync.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 09:34:20 -05:00
bba678568a Polish: simplify formatTimestamp and tone down export button
Replace try/catch with isNaN guard in the HTML exporter's
formatTimestamp, matching the same cleanup applied client-side.

Downgrade the export button from btn-primary to btn-secondary so it
doesn't compete visually with the main content area. The primary blue
gradient was overly prominent for a utility action.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 09:26:35 -05:00
c0e4158b77 Add time gap indicators and hash anchor navigation to SessionViewer
Time gaps:
- Insert a horizontal divider with duration label ("12m later",
  "1h 30m later") between consecutive visible messages separated
  by more than 5 minutes
- Computed during the display list build pass alongside redacted
  dividers, so no additional traversal is needed

Hash anchor navigation:
- Each message div now has id="msg-{uuid}" for deep linking
- On load, if the URL contains a #msg-* hash, scroll that message
  into view with smooth centering and a 3-second highlight ring
- Works with the copy-link feature added to MessageBubble headers

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 09:26:27 -05:00
0f3739605c Add language labels and copy-to-clipboard for code blocks
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>
2026-01-30 09:26:18 -05:00
1dc178f59f Overhaul MessageBubble with collapsible sections, diff rendering, and header actions
Collapsible blocks:
- Thinking, tool_call, and tool_result messages start collapsed by default
- Chevron toggle in the header expands/collapses content
- Collapsed preview shows line count (thinking), tool name (tool_call),
  or first line truncated to 120 chars (tool_result)
- Collapsed blocks skip expensive markdown/highlight rendering entirely

Diff rendering:
- Detect unified diff content via hunk header + add/delete line heuristics
  (requires both @@ headers AND +/- lines to avoid false positives on
  YAML or markdown lists with leading dashes)
- Render diffs with color-coded line classes: green additions, red
  deletions, blue hunk headers, and muted meta/header lines
- Add full diff-view CSS with background tints and block-level spans

Header actions (appear on hover):
- Copy link button: copies a #msg-{uuid} anchor URL to clipboard with
  a checkmark confirmation animation
- Redaction toggle button: replaces the previous whole-card onClick
  handler with an explicit eye-slash icon button, colored red when
  selected — more discoverable and less accident-prone

Style adjustments:
- Raise dimmed message opacity from 0.2/0.45 to 0.35/0.65 for better
  readability during search filtering
- Fix btn-secondary hover border using explicit rgba value instead of
  Tailwind opacity modifier (which was generating invalid CSS)
- Position copy button below language label when both are present
- Simplify formatTimestamp with isNaN guard instead of try/catch
- Use fixed h-10 header height for consistent vertical alignment
- Brighten user and assistant message backgrounds (bg-surface-overlay)
  to visually distinguish them from other message types

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 09:26:09 -05:00
50b29ff0a2 Display session duration in sidebar and simplify date formatting
Show human-readable session duration (e.g. "23m", "1h 15m") in the
session list metadata row when duration > 0. Add formatSessionDuration
helper that handles sub-minute, minute-only, and hour+minute ranges.

Also replace try/catch in formatDate with an isNaN guard on the parsed
Date, which is more idiomatic and avoids swallowing unrelated errors.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 09:25:51 -05:00
9716091ecc Optimize markdown rendering: skip highlightAuto, fix entity-safe highlighting
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 (&amp; &lt; etc.) before applying search regex, so searching
for 'amp' does not break &amp; 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>
2026-01-30 01:10:22 -05:00
6a4e22f1f8 Add search navigation with match cycling, keyboard shortcuts, and minimap
Implement full-featured search navigation across the session viewer:

App.tsx: Compute matchIndices (filtered message indices matching the query),
track currentMatchPosition state, and provide goToNext/goToPrev callbacks.
Add scroll tracking via ResizeObserver + scroll events for minimap viewport.
Restructure toolbar layout with centered search bar and right-aligned export.
Pass focusedIndex to SessionViewer for scroll-into-view behavior.

SearchBar: Redesign as a unified search container with integrated match count
badge, prev/next navigation arrows, clear button, and keyboard hint (/).
Add keyboard shortcuts: Enter/Shift+Enter for next/prev, Ctrl+G/Ctrl+Shift+G
for navigation, Escape to clear and blur. Show 'X/N' position indicator and
'No results' state.

SessionViewer: Add data-msg-index attributes for scroll targeting via
querySelector instead of individual refs. Memoize displayItems list. Add
MessageSkeleton component for loading state. Add empty state illustrations
with icons and descriptive text. Apply staggered fade-in animations and
search-match-focused outline to the active match.

SearchMinimap: New component rendering match positions as amber ticks on a
narrow strip overlaying the scroll area's right edge. Includes viewport
position indicator and click-to-jump behavior.

Add unit tests for SearchMinimap: empty/zero states, tick rendering,
active tick styling, viewport indicator, and click handler.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 01:10:08 -05:00
15a312d98c Redesign all client components with dark theme, polish, and UX improvements
MessageBubble: Replace border-left colored bars with rounded cards featuring
accent strips, category dot indicators, and timestamp display. Use shared
escapeHtml. Render tool_result, hook_progress, and file_snapshot as
preformatted text instead of markdown (avoids expensive marked.parse on
large JSON/log blobs).

ExportButton: Add state machine (idle/exporting/success/error) with animated
icons, gradient backgrounds, and auto-reset timers. Replace alert() with
inline error state.

FilterPanel: Add collapsible panel with category dot colors, enable count
badge, custom checkbox styling, and smooth animations.

SessionList: Replace text loading state with skeleton placeholders. Add
empty state illustration with descriptive text. Style session items as
rounded cards with hover/selected states, glow effects, and staggered
entry animations. Add project name decode explanation comment.

RedactedDivider: Add eye-slash SVG icon, red accent color, and styled
dashed lines replacing plain text divider.

useFilters: Remove unused exports (setAllCategories, setPreset, undoRedaction,
clearAllRedactions, selectAllVisible, getMatchCount) to reduce hook surface
area. Match counting moved to App component for search navigation.

SessionList.test: Update assertions for skeleton loading state and expanded
empty state text.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 01:09:41 -05:00
40b3ccf33e Add dark design system with CSS custom properties and refined typography
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>
2026-01-30 01:09:24 -05:00
ecd63cd1c3 Add React client: session browser, message viewer, filters, search, redaction, export
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>
2026-01-29 22:56:37 -05:00