claude_session_viewer/lib/models/content_block.dart
Mathias Beaulieu-Duncan 364877d376 Initial commit: Claude Code session viewer (Flutter macOS)
A desktop app that parses Claude Code .jsonl session logs and provides
a rich UI for exploring conversations, tool usage, subagents, and token
consumption. Features include project browser with auto-discovery of
~/.claude/projects, conversation timeline with inline subagent expansion,
agents overview, toolbelt chart, and token usage dashboard.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 16:17:23 -04:00

117 lines
3.0 KiB
Dart

class ContentBlock {
final String type;
final Map<String, dynamic> raw;
ContentBlock({required this.type, required this.raw});
factory ContentBlock.fromJson(Map<String, dynamic> json) {
final type = json['type'] as String? ?? 'unknown';
switch (type) {
case 'text':
return TextBlock.fromJson(json);
case 'thinking':
return ThinkingBlock.fromJson(json);
case 'tool_use':
return ToolUseBlock.fromJson(json);
default:
return ContentBlock(type: type, raw: json);
}
}
}
class TextBlock extends ContentBlock {
final String text;
TextBlock({required this.text, required Map<String, dynamic> raw})
: super(type: 'text', raw: raw);
factory TextBlock.fromJson(Map<String, dynamic> json) {
return TextBlock(
text: json['text'] as String? ?? '',
raw: json,
);
}
}
class ThinkingBlock extends ContentBlock {
final String thinking;
final String? signature;
ThinkingBlock({
required this.thinking,
this.signature,
required Map<String, dynamic> raw,
}) : super(type: 'thinking', raw: raw);
factory ThinkingBlock.fromJson(Map<String, dynamic> json) {
return ThinkingBlock(
thinking: json['thinking'] as String? ?? '',
signature: json['signature'] as String?,
raw: json,
);
}
}
class ToolUseBlock extends ContentBlock {
final String id;
final String name;
final Map<String, dynamic> input;
ToolResultData? linkedResult;
ToolUseBlock({
required this.id,
required this.name,
required this.input,
this.linkedResult,
required Map<String, dynamic> raw,
}) : super(type: 'tool_use', raw: raw);
factory ToolUseBlock.fromJson(Map<String, dynamic> json) {
return ToolUseBlock(
id: json['id'] as String? ?? '',
name: json['name'] as String? ?? '',
input: (json['input'] as Map<String, dynamic>?) ?? {},
raw: json,
);
}
bool get isAgentCall => name == 'Agent';
String? get subagentType => isAgentCall ? input['subagent_type'] as String? : null;
String? get agentDescription => isAgentCall ? input['description'] as String? : null;
String? get agentPrompt => isAgentCall ? input['prompt'] as String? : null;
}
class ToolResultData {
final String toolUseId;
final dynamic content;
final bool isError;
final Map<String, dynamic> raw;
ToolResultData({
required this.toolUseId,
this.content,
this.isError = false,
required this.raw,
});
String get textContent {
if (content is String) return content;
if (content is List) {
return (content as List)
.where((c) => c is Map && c['type'] == 'text')
.map((c) => c['text'] as String? ?? '')
.join('\n');
}
return content?.toString() ?? '';
}
factory ToolResultData.fromJson(Map<String, dynamic> json) {
return ToolResultData(
toolUseId: json['tool_use_id'] as String? ?? '',
content: json['content'],
isError: json['is_error'] as bool? ?? false,
raw: json,
);
}
}