Beagle uses a flexible content system to display log details. Each content type serves a specific purpose and can be combined to create rich, informative detail views.
Available Content Types
Beagle provides eight content types, each with unique properties and use cases:
Text
Displays text with optional formatting options.
interface TextContent {
kind : 'text' ;
text : string ;
variant ?: 'body' | 'caption' | 'heading' ; // Typography variant
bold ?: boolean ; // Bold text
selectable ?: boolean ; // Enable text selection
lines ?: number ; // Maximum number of lines
}
Basic Text
Selectable Code
{
kind : 'text' ,
text : 'This is a simple text message'
}
{
kind : 'text' ,
text : error . stack ,
selectable : true ,
lines : 10
}
JSON
Displays JSON data in an interactive, collapsible tree view.
interface JsonContent {
kind : 'json' ;
data : Record < string , any >;
expanded ?: boolean ; // Start expanded or collapsed
}
Example from NetworkingLogPlugin:
{
kind : 'json' ,
data : log . params ,
expanded : true
}
JSON content automatically handles nested objects and arrays, providing an interactive exploration experience.
Label
Displays a label-value pair, perfect for key-value data.
interface LabelContent {
kind : 'label' ;
label : string ;
value : string ;
}
Example from NetworkingLogPlugin:
[
{ kind: 'label' , label: 'URL' , value: request . url },
{ kind: 'label' , label: 'Method' , value: request . method },
{ kind: 'label' , label: 'Status' , value: response . status . toString () },
{ kind: 'label' , label: 'Duration' , value: ` ${ response . duration } ms` }
]
Loading
Displays a loading indicator for pending operations.
interface LoadingContent {
kind : 'loading' ;
label ?: string ; // Optional message
size ?: number ; // Spinner size
}
Example from NetworkingLogPlugin:
{
kind : 'loading' ,
label : 'The request is still pending'
}
Section
Groups related content under a collapsible header.
interface SectionContent {
kind : 'section' ;
key : string ; // Unique identifier
title : string ; // Section header text
expanded ?: boolean ; // Start expanded or collapsed
children : Content []; // Content within the section
}
Example from NetworkingLogPlugin:
private provideHeadersContent (
headers : NetworkingHeaders | undefined ,
suffix : string
): SectionContent {
return {
key: ` ${ suffix } _headers` ,
kind: 'section' ,
title: 'Headers' ,
children: headers
? Object . entries ( headers ). map (([ key , value ]) => ({
kind: 'label' ,
label: key ,
value ,
}))
: [
{
kind: 'text' ,
text: 'No headers' ,
variant: 'caption' ,
},
],
};
}
Box
Arranges content in a horizontal or vertical layout with flexible positioning.
interface BoxContent {
kind : 'box' ;
key : string ; // Unique identifier
direction ?: 'row' | 'column' ;
justifyContent ?: 'flex-start' | 'center' | 'flex-end' | 'space-between' | 'space-around' ;
children : Content [];
}
Example from NetworkingLogPlugin:
provideCardFooter ( log : NetworkingLog ): BoxContent {
const children : Content [] = [
{
kind: 'text' ,
text: log . host ,
variant: 'caption' ,
},
];
if ( log . response ) {
children . push ({
kind: 'text' ,
text: ` ${ log . response . duration } ms` ,
variant: 'caption' ,
});
}
return {
key: 'footer' ,
kind: 'box' ,
direction: 'row' ,
justifyContent: 'space-between' ,
children ,
};
}
List
Displays a vertical list of content items.
interface ListContent {
kind : 'list' ;
key : string ;
children : ( Content | SectionContent )[];
}
Example from ErrorLogPlugin:
const listItems : Content [] = [
{ kind: 'label' , label: 'Name' , value: error . name },
{ kind: 'label' , label: 'Message' , value: error . message },
];
if ( error . stack ) {
listItems . push ({
kind: 'text' ,
text: 'Stack' ,
variant: 'body' ,
bold: true ,
});
listItems . push ({ kind: 'text' , text: error . stack , selectable: true });
}
return {
key: 'error' ,
kind: 'list' ,
children: listItems ,
};
Tab Bar
Displays multiple tabs, each containing a list of content.
interface Tab {
title : string ;
content : ListContent ;
}
interface TabBarContent {
kind : 'tab-bar' ;
tabs : Tab [];
}
Example from NetworkingLogPlugin:
provideDetailContent ( log : NetworkingLog ): DetailContent {
return {
kind: 'tab-bar' ,
tabs: [
{
title: 'Info' ,
content: this . provideInfoContent ( log ),
},
{
title: 'Request' ,
content: this . provideRequestContent ( log . request ),
},
{
title: 'Response' ,
content: this . provideResponseContent ( log . response ),
},
],
};
}
When to Use Each Type
Text
Use for:
Simple messages
Headers and titles
Stack traces
Any string content that needs formatting
JSON
Use for:
Request/response bodies
Configuration objects
Event parameters
Any structured data
Label
Use for:
Key-value pairs
Metadata (URL, status, duration)
Object properties
Configuration values
Loading
Use for:
Pending network requests
Async operations in progress
Loading states
Section
Use for:
Grouping related content
Collapsible headers/bodies
Organizing large detail views
Box
Use for:
Horizontal layouts (e.g., footer with left and right content)
Custom spacing and alignment
Flexible positioning
List
Use for:
Vertical content arrangement
Main container for detail views
Combining multiple content types
Tab Bar
Use for:
Multiple related views (Info, Request, Response)
Organizing large amounts of data
Different aspects of the same log
Building Detail Views: Real Examples
Here are complete examples from Beagle’s built-in plugins:
Message Plugin
Error Plugin
Analytics Plugin
provideDetailContent ( log : MessageLog ): DetailContent {
return {
key: 'message' ,
kind: 'list' ,
children: [
{
kind: 'text' ,
text: log . message ,
selectable: true ,
},
],
};
}
Best Practices
Combine content types to create rich, informative views. Use sections to organize, labels for metadata, JSON for data, and text for messages.
Always provide a unique key for section, box, list, and tab-bar content types. This ensures proper React rendering and state management.
Don’t nest tab bars . The tab-bar type should only appear at the root level of provideDetailContent.
Content Composition Tips
Start with the container : Choose between list (single view) or tab-bar (multiple views)
Group with sections : Use sections to organize related content under collapsible headers
Label metadata first : Put key-value pairs at the top for quick scanning
JSON for complex data : Use JSON content for nested objects and arrays
Text for messages : Use text content for strings, with appropriate formatting
Loading for pending : Show loading states for async operations