blob: 3817ac4764f5b15dacc613cf0701b30f72306a88 [file]
import 'package:jaspr/dom.dart';
import 'package:jaspr/jaspr.dart';
import '../../../utils/styles.dart';
import '../models/agent_tool_event.dart';
import '../models/agent_tool_event_presenter.dart';
/// A component that renders a single row describing a tool call executed by the agent.
///
/// Displays the action description in either running or finished state.
/// For screenshot tool calls, also displays a thumbnail of the captured image.
class AgentToolCallRow extends StatelessComponent {
/// Creates an [AgentToolCallRow] component.
const AgentToolCallRow({required this.event, super.key});
/// The tool execution event details to render.
final AgentToolEvent event;
@override
Component build(BuildContext context) {
final presentation = AgentToolEventPresenter.present(event);
final isRunning = event.status == ToolStatus.running;
final screenshot = event.screenshotBase64;
return div(
classes: 'agent-tool-call-row${isRunning ? ' running' : ' finished'}',
attributes: {
'role': 'status',
'aria-label': presentation.ariaLabel,
},
[
span(
classes: 'agent-tool-call-text',
[Component.text(presentation.actionText)],
),
if (screenshot != null && !isRunning)
div(classes: 'agent-tool-screenshot-container', [
img(
classes: 'agent-tool-screenshot-thumb',
src: 'data:image/png;base64,$screenshot',
attributes: {
'alt': 'Screenshot of ${event.summary}',
},
),
]),
],
);
}
@css
static List<StyleRule> get styles => [
css('.agent-tool-call-row').styles(
display: .flex,
padding: Padding.symmetric(vertical: 4.px, horizontal: 0.px),
margin: Margin.zero,
flexDirection: .column,
alignItems: .start,
gap: Gap.all(8.px),
color: colorOnSurface,
fontSize: 12.px,
),
css('.agent-tool-call-row.running').styles(
raw: {
'animation': 'agent-tool-pulse 1.5s ease-in-out infinite',
},
),
css('@keyframes agent-tool-pulse').styles(
raw: {
'0%': 'opacity: 0.6;',
'50%': 'opacity: 1.0;',
'100%': 'opacity: 0.6;',
},
),
css('.agent-tool-call-text').styles(
fontFamily: const .list([FontFamily('Inter'), FontFamilies.sansSerif]),
),
css('.agent-tool-screenshot-container').styles(
display: .flex,
padding: Padding.all(4.px),
border: Border.all(color: colorBorder, width: 1.px),
radius: .circular(6.px),
overflow: .hidden,
backgroundColor: colorContainerLow,
),
css('.agent-tool-screenshot-thumb').styles(
display: .block,
radius: .circular(4.px),
raw: {
'max-width': '240px',
'max-height': '180px',
'object-fit': 'contain',
},
),
];
}