blob: 3d3874888b44a72e2b2c6322e7b759d4275fc5ba [file]
import 'dart:convert';
import 'package:schemantic/schemantic.dart';
import '../../../shared/logic/app_event_bus.dart';
import '../../models/agent_tool_event.dart';
import '../../models/agent_tool_id.dart';
import '../base/agent_tool.dart';
import '../tool_schemas.dart';
/// Simulates a tap/click on a widget by its nodeId.
class TapWidgetTool extends AgentTool<TapWidgetInput, TapWidgetOutput> {
const TapWidgetTool({
required this.commandBus,
});
final AppCommandBus commandBus;
@override
AgentToolId get toolId => AgentToolId.tapWidget;
@override
String get description =>
'Simulate a tap gesture on a specific widget in the running app preview using its node ID. '
'Node IDs can be found via getWidgetTree or findWidget.';
@override
SchemanticType<TapWidgetInput> get inputSchema => TapWidgetInput.$schema;
@override
SchemanticType<TapWidgetOutput> get outputSchema => TapWidgetOutput.$schema;
Future<String?> _invokeExtension(String method, Map<String, String> args) {
return commandBus.dispatchAsync(
InvokePreviewExtensionCommand(
method: method,
args: args,
),
);
}
@override
Future<TapWidgetOutput> run(TapWidgetInput input) async {
final state = await commandBus.dispatchAsync(RequestPreviewStateCommand());
if (!state.hasActiveRuntime) {
return TapWidgetOutput(
success: false,
error: 'No app preview is running. Use controlApp to start the preview first.',
);
}
try {
final responseStr = await _invokeExtension(
'ext.vibepad.tapWidget',
{'id': input.nodeId},
);
if (responseStr == null || responseStr.isEmpty) {
return TapWidgetOutput(
success: false,
error: 'Could not tap the widget. No response from the running app.',
);
}
final Map<String, dynamic> decoded = jsonDecode(responseStr) as Map<String, dynamic>;
final success = decoded['success'] as bool? ?? false;
if (!success) {
return TapWidgetOutput(
success: false,
error: decoded['error'] as String? ?? 'Failed to tap widget.',
);
}
return TapWidgetOutput(
success: true,
);
} catch (e) {
return TapWidgetOutput(
success: false,
error: 'Failed to tap widget "${input.nodeId}": $e',
);
}
}
@override
AgentToolEvent mapToToolEvent(
TapWidgetInput input,
TapWidgetOutput output,
) {
return createToolEvent(
status: output.success ? ToolStatus.success : ToolStatus.error,
summary: output.success ? 'Tapped widget "${input.nodeId}"' : 'Failed to tap widget "${input.nodeId}"',
errorMessage: output.error,
);
}
}