| 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, |
| ); |
| } |
| } |