Replaced file_picker's Load/Browse with a custom NativePickerRegistrar Swift plugin that opens NSOpenPanel with showsHiddenFiles = true. The file_picker package hardcodes this to false, making hidden folders like ~/.claude invisible in its dialogs. Changes: - New NativePickerRegistrar.swift: custom NSOpenPanel with hidden files - New NativePicker Dart service using method channel - Browse: only shows folders (canChooseFiles=false), hidden visible - Load: only shows .jsonl files, hidden folders visible - Registered via AppDelegate.applicationDidFinishLaunching - Removed file_picker dependency from home_screen imports - Fixed all info-level lint issues (super params, null-aware, doc comment) - Signed, notarized, stapled DMG |
||
|---|---|---|
| lib | ||
| macos | ||
| test | ||
| .gitignore | ||
| .metadata | ||
| analysis_options.yaml | ||
| LICENSE | ||
| pubspec.lock | ||
| pubspec.yaml | ||
| README.md | ||
Claude Session Viewer
A macOS desktop app for browsing and analyzing your Claude AI conversation sessions. Explore your prompts, assistant responses, tool usage, token consumption, and subagent activity — all in a fast, searchable interface.
For Everyone
What It Does
If you use Claude with the CLI (claude), every conversation is saved as a .jsonl file on your Mac. Claude Session Viewer lets you open those files and browse them with:
- Timeline view — See your conversation as a visual tree with user prompts, assistant responses, and tool calls all laid out chronologically
- Search & filter — Find specific messages, filter by type (user/assistant/system), or narrow down to a specific subagent
- Tool usage analytics — See which tools were used, how often, and with what arguments
- Token tracking — Break down token consumption by agent (input, output, cache hits)
- Subagent inspection — Drill into subagent conversations that were spawned during a session
Installation
- Download the latest
Claude Session Viewer.dmgfrom the Releases page - Double-click the DMG
- Drag Claude Session Viewer to your Applications folder
- Launch it — no Gatekeeper warnings, the app is signed and notarized by Apple
Quick Start
- Open the app
- It automatically scans
~/.claude/projectsfor your session files - Pick a project → pick a session → browse the timeline
- Or use File → Open to load any
.jsonlfile directly
Tips
- Use the search bar in the timeline to find specific messages or tool calls
- Click the type toggles (User / Assistant / System) to filter what's shown
- Use the agent dropdown to focus on a specific subagent's activity
- Expand tool use cards to see arguments, results, and inline subagent conversations
- Expand Thinking blocks to see Claude's reasoning (when available)
For Developers
Architecture
lib/
├── main.dart # Entry point, Provider setup
├── models/
│ ├── agent_info.dart # AgentInfo + SessionLog aggregates
│ ├── content_block.dart # ContentBlock hierarchy (Text, Thinking, ToolUse, ToolResult)
│ ├── log_entry.dart # LogEntry hierarchy (User, Assistant, System, Progress, FileSnapshot)
│ └── token_usage.dart # TokenUsage value type
├── providers/
│ └── session_provider.dart # ChangeNotifier with cached filtering + debounced search
├── services/
│ └── jsonl_parser.dart # Isolate-based JSONL parser + session builder
├── screens/
│ ├── home/ # Project/session browser (scans ~/.claude/projects)
│ ├── timeline/ # Flat virtualized timeline with tree connectors
│ ├── agents/ # Agent analytics cards
│ ├── tokens/ # Token usage dashboard (pie chart + data table)
│ └── toolbelt/ # Tool usage bar chart + per-tool details
├── theme/
│ └── app_theme.dart # Dark theme + color constants
└── widgets/
├── common/ # ExpandableCard (lazy expand), JsonTreeView
└── navigation/ # AppShell (IndexedStack), Sidebar
Data Flow
FilePicker → SessionProvider.loadSession()
→ Isolate.run(JsonlParser._parseInIsolate) # CPU work off main thread
→ jsonDecode per line + LogEntry.fromJson
→ _linkToolResults() + _buildAgents()
→ ParseResult(SessionLog, List<ParseError>)
→ SessionProvider caches filteredEntries
→ Screens consume via context.select<SessionProvider, T>()
Key Design Decisions
| Decision | Rationale |
|---|---|
| Single ChangeNotifier | App has one data source (a session file). No need for Riverpod/Bloc complexity. |
| Isolate-based parsing | 50K-line JSONL files parse in 2-4s — must not block the UI thread. |
| Flat virtualized timeline | Flattened turns into List<_TimelineItem> so ListView.builder recycles every card independently. |
| Cached filter results | _FilterKey-based invalidation avoids O(n) recomputation on every rebuild. |
| Lazy ExpandableCard | AnimatedCrossFade builds both children eagerly. Replaced with SizeTransition + _hasBeenExpanded guard. |
| IndexedStack navigation | Preserves scroll position and state across tab switches. |
| No code generation | 17 source files, single-user tool — hand-written fromJson is fine. |
Building from Source
Requirements: Flutter 3.29+ / Dart 3.10+, macOS 13.0+, Xcode 16+
# Clone
git clone https://github.com/your-org/claude_session_viewer.git
cd claude_session_viewer
# Install dependencies
flutter pub get
# Run in debug mode
flutter run -d macos
# Build release
flutter build macos --release
Building a Signed & Notarized DMG
The project is pre-configured for Developer ID distribution. Set your team ID in macos/Runner.xcodeproj/project.pbxproj and macos/Runner/Configs/AppInfo.xcconfig, then:
# Build
flutter build macos --release
# Re-sign with hardened runtime (inside-out)
APP="build/macos/Build/Products/Release/Claude Session Viewer.app"
SIGN_ID="Developer ID Application: Your Name (TEAMID)"
find "$APP/Contents/Frameworks" -name "*.framework" -maxdepth 1 | while read fw; do
codesign --force --deep --sign "$SIGN_ID" --timestamp --options runtime \
--entitlements macos/Runner/Release.entitlements "$fw"
done
codesign --force --deep --sign "$SIGN_ID" --timestamp --options runtime \
--entitlements macos/Runner/Release.entitlements "$APP"
# Create DMG
hdiutil create -volname "Claude Session Viewer" \
-srcfolder "$APP" -ov -format UDZO \
"build/Claude Session Viewer.dmg"
# Sign DMG
codesign --sign "$SIGN_ID" --timestamp "build/Claude Session Viewer.dmg"
# Notarize
xcrun notarytool submit "build/Claude Session Viewer.dmg" \
--keychain-profile "notarytool" --wait
# Staple ticket
xcrun stapler staple "build/Claude Session Viewer.dmg"
Dependencies
| Package | Purpose |
|---|---|
provider |
State management (ChangeNotifier) |
file_picker |
Native file picker dialog |
flutter_markdown |
Markdown rendering in assistant messages |
fl_chart |
Pie chart (tokens) + bar chart (tool usage) |
google_fonts |
Inter font family |
intl |
Number/date formatting |
Entitlements (macOS Sandbox)
The release build runs in the macOS App Sandbox with:
com.apple.security.app-sandbox— enabledcom.apple.security.files.user-selected.read-only— open files via dialogcom.apple.security.files.home-directory.read-only— scan~/.claude/projects
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Commit your changes
- Open a pull request
Note: This project is non-commercial — contributions must respect the CC BY-NC 4.0 license.
License
This project is licensed under Creative Commons Attribution-NonCommercial 4.0 International.
You are free to use, modify, and distribute this software for personal and non-commercial purposes. Commercial use, resale, or monetization is not permitted without written permission.
© 2026 Svrnty. All rights reserved.