diff --git a/lib/screens/home_page/editor_pane/agentic_ui_elements/ai_ui_designer/ai_ui_desginer_widgets.dart b/lib/screens/home_page/editor_pane/agentic_ui_elements/ai_ui_designer/ai_ui_desginer_widgets.dart deleted file mode 100644 index 5c77758c..00000000 --- a/lib/screens/home_page/editor_pane/agentic_ui_elements/ai_ui_designer/ai_ui_desginer_widgets.dart +++ /dev/null @@ -1,530 +0,0 @@ -import 'dart:convert'; -import 'package:apidash/consts.dart'; -import 'package:apidash/providers/collection_providers.dart'; -import 'package:apidash/services/agentic_services/agent_caller.dart'; -import 'package:apidash/widgets/widget_sending.dart'; -import 'package:apidash_design_system/apidash_design_system.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:genai/agentic_engine/blueprint.dart'; -import 'package:stac/stac.dart' as stac; -import '../../../../../services/agentic_services/agents/agents.dart'; - -void showCustomDialog(BuildContext context, Widget dialogContent) { - showDialog( - context: context, - builder: (BuildContext context) { - return Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12.0), - ), - child: dialogContent, - ); - }, - ); -} - -class GenerateUIDialog extends ConsumerStatefulWidget { - final String content; - const GenerateUIDialog({ - super.key, - required this.content, - }); - - @override - ConsumerState createState() => _GenerateUIDialogState(); -} - -class _GenerateUIDialogState extends ConsumerState { - int index = 0; - TextEditingController controller = TextEditingController(); - - String generatedSDUI = '{}'; - - Future generateSDUICode(String apiResponse) async { - try { - setState(() { - index = 1; //Induce Loading - }); - //STEP 1: RESPONSE_ANALYSER (call the Semantic Analysis & IRGen Bots in parallel) - final step1Res = await Future.wait([ - APIDashAgentCaller.instance.call( - ResponseSemanticAnalyser(), - ref: ref, - input: AgentInputs(query: apiResponse), - ), - APIDashAgentCaller.instance.call( - IntermediateRepresentationGen(), - ref: ref, - input: AgentInputs(variables: { - 'VAR_API_RESPONSE': apiResponse, - }), - ), - ]); - final SA = step1Res[0]?['SEMANTIC_ANALYSIS']; - final IR = step1Res[1]?['INTERMEDIATE_REPRESENTATION']; - - if (SA == null || IR == null) { - setState(() { - index = 0; - }); - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text( - "Preview Generation Failed!", - style: TextStyle(color: Colors.white), - ), - backgroundColor: Colors.redAccent, - )); - return null; - } - - print("Semantic Analysis: $SA"); - print("Intermediate Representation: $IR"); - - //STEP 2: STAC_GEN (Generate the SDUI Code) - - final sduiCode = await APIDashAgentCaller.instance.call( - StacGenBot(), - ref: ref, - input: AgentInputs(variables: { - 'VAR_RAW_API_RESPONSE': apiResponse, - 'VAR_INTERMEDIATE_REPR': IR, - 'VAR_SEMANTIC_ANALYSIS': SA, - }), - ); - final stacCode = sduiCode?['STAC']?.toString(); - if (stacCode == null) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text( - "Preview Generation Failed!", - style: TextStyle(color: Colors.white), - ), - backgroundColor: Colors.redAccent, - )); - setState(() { - index = 0; - }); - return null; - } - return sduiCode['STAC'].toString(); - } catch (e) { - String errMsg = 'Unexpected Error Occured'; - if (e.toString().contains('NO_DEFAULT_LLM')) { - errMsg = "Please Select Default AI Model in Settings"; - } - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text( - errMsg, - style: TextStyle(color: Colors.white), - ), - backgroundColor: Colors.redAccent, - )); - Navigator.pop(context); - } - } - - Future modifySDUICode(String modificationRequest) async { - setState(() { - index = 1; //Induce Loading - }); - - final res = await APIDashAgentCaller.instance.call( - StacModifierBot(), - ref: ref, - input: AgentInputs(variables: { - 'VAR_CODE': generatedSDUI, - 'VAR_CLIENT_REQUEST': modificationRequest, - }), - ); - - final SDUI = res?['STAC']; - - if (SDUI == null) { - setState(() { - index = 2; - }); - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text( - "Modification Request Failed!", - style: TextStyle(color: Colors.white), - ), - backgroundColor: Colors.redAccent, - )); - return; - } - setState(() { - generatedSDUI = SDUI; - index = 2; - }); - } - - @override - Widget build(BuildContext context) { - return IndexedStack( - index: index, - children: [ - FrameWorkSelectorPage( - content: widget.content, - onNext: (apiResponse, targetLanguage) async { - print("Generating SDUI Code"); - final sdui = await generateSDUICode(apiResponse); - if (sdui == null) return; - setState(() { - index = 2; - generatedSDUI = sdui; - }); - }, - ), - SizedBox( - width: MediaQuery.of(context).size.width * 0.6, - child: Center( - child: Padding( - padding: const EdgeInsets.only(top: 40.0), - child: Container( - height: 500, - child: SendingWidget( - startSendingTime: DateTime.now(), - showTimeElapsed: false, - ), - ), - ), - ), - ), - SDUIPreviewPage( - key: ValueKey(generatedSDUI.hashCode), - onModificationRequestMade: modifySDUICode, - sduiCode: generatedSDUI, - ) - ], - ); - } -} - -class FrameWorkSelectorPage extends StatefulWidget { - final String content; - final Function(String, String) onNext; - const FrameWorkSelectorPage( - {super.key, required this.content, required this.onNext}); - - @override - State createState() => _FrameWorkSelectorPageState(); -} - -class _FrameWorkSelectorPageState extends State { - String? selectedFramework; - TextEditingController controller = TextEditingController(); - - @override - void initState() { - controller.text = widget.content; - super.initState(); - } - - @override - Widget build(BuildContext context) { - final textContainerdecoration = BoxDecoration( - color: Color.alphaBlend( - (Theme.of(context).brightness == Brightness.dark - ? Theme.of(context).colorScheme.onPrimaryContainer - : Theme.of(context).colorScheme.primaryContainer) - .withOpacity(kForegroundOpacity), - Theme.of(context).colorScheme.surface), - border: Border.all( - color: Theme.of(context).colorScheme.surfaceContainerHighest), - borderRadius: kBorderRadius8, - ); - - return Container( - width: MediaQuery.of(context).size.width * 0.6, // Large dialog - padding: EdgeInsets.all(20), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Container( - width: double.maxFinite, - padding: kP8, - decoration: textContainerdecoration, - child: SingleChildScrollView( - child: TextField( - controller: controller, - maxLines: null, - style: kCodeStyle, - ), - ), - ), - ), - kVSpacer20, - // Text( - // "Select Framework", - // style: TextStyle( - // color: Colors.white, - // fontSize: 18, - // fontWeight: FontWeight.bold, - // ), - // ), - // SizedBox(height: 10), - // DropdownButtonFormField( - // dropdownColor: Color(0xFF2D2D2D), - // decoration: InputDecoration( - // filled: true, - // fillColor: Color(0xFF2D2D2D), - // border: OutlineInputBorder( - // borderRadius: BorderRadius.circular(8.0), - // ), - // ), - // value: selectedFramework, - // items: ["Flutter", "ReactJS"].map((String value) { - // return DropdownMenuItem( - // value: value, - // child: Text( - // value, - // style: TextStyle(color: Colors.white), - // ), - // ); - // }).toList(), - // onChanged: (newValue) { - // selectedFramework = newValue; - // setState(() {}); - // }, - // ), - // kVSpacer20, - Align( - alignment: Alignment.centerRight, - child: FilledButton.tonalIcon( - style: FilledButton.styleFrom( - padding: kPh12, - minimumSize: const Size(44, 44), - ), - onPressed: () { - widget.onNext(controller.value.text, "FLUTTER"); - }, - icon: Icon( - Icons.generating_tokens, - ), - label: const SizedBox( - child: Text( - kLabelGenerateUI, - ), - ), - ), - ), - ], - ), - ); - } -} - -class SDUIPreviewPage extends ConsumerStatefulWidget { - final String sduiCode; - final Function(String) onModificationRequestMade; - const SDUIPreviewPage({ - super.key, - required this.onModificationRequestMade, - required this.sduiCode, - }); - - @override - ConsumerState createState() => _SDUIPreviewPageState(); -} - -class _SDUIPreviewPageState extends ConsumerState { - bool exportingCode = false; - String modificationRequest = ""; - - exportCode() async { - setState(() { - exportingCode = true; - }); - final ans = await APIDashAgentCaller.instance.call( - StacToFlutterBot(), - ref: ref, - input: AgentInputs( - variables: {'VAR_CODE': widget.sduiCode}, - ), - ); - final exportedCode = ans?['CODE']; - - if (exportedCode == null) { - setState(() { - exportingCode = false; - }); - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text( - "Export Failed", - style: TextStyle(color: Colors.white), - ), - backgroundColor: Colors.redAccent, - )); - print("exportCode: Failed; ABORTING"); - return; - } - - Clipboard.setData(ClipboardData(text: ans['CODE'])); - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text("Copied to clipboard!"))); - setState(() { - exportingCode = false; - }); - } - - @override - Widget build(BuildContext context) { - return Container( - width: MediaQuery.of(context).size.width * 0.6, // Large dialog - padding: EdgeInsets.all(20), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Generated Component", - style: TextStyle( - fontSize: 20, - ), - ), - kVSpacer20, - Expanded( - child: Center( - child: Container( - padding: EdgeInsets.all(20), - decoration: BoxDecoration( - border: Border.all(color: Colors.white), - ), - child: widget.sduiCode == '{}' - ? SizedBox() - : StacRenderer( - stacRepresentation: widget.sduiCode, - ), - ), - ), - ), - kVSpacer20, - if (!exportingCode) ...[ - Container( - width: double.infinity, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(15), - ), - child: ADOutlinedTextField( - hintText: 'Any Modifications?', - onChanged: (z) { - setState(() { - modificationRequest = z; - }); - }, - maxLines: 3, // Makes the text box taller - ), - ), - kVSpacer20, - ], - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Align( - alignment: Alignment.centerRight, - child: (exportingCode) - ? Container( - child: CircularProgressIndicator( - strokeWidth: 1, - ), - margin: EdgeInsets.only(right: 10), - ) - : FilledButton.tonalIcon( - style: FilledButton.styleFrom( - padding: kPh12, - minimumSize: const Size(44, 44), - ), - onPressed: exportCode, - icon: Icon( - Icons.download, - ), - label: const SizedBox( - child: Text( - "Export Code", - ), - ), - ), - ), - kHSpacer10, - if (!exportingCode) - Align( - alignment: Alignment.centerRight, - child: FilledButton.tonalIcon( - style: FilledButton.styleFrom( - padding: kPh12, - minimumSize: const Size(44, 44), - ), - onPressed: () { - if (modificationRequest.isNotEmpty) { - widget.onModificationRequestMade(modificationRequest); - } - }, - icon: Icon( - Icons.generating_tokens, - ), - label: const SizedBox( - child: Text( - "Make Modifications", - ), - ), - ), - ), - ], - ), - ], - ), - ); - } -} - -class StacRenderer extends StatelessWidget { - final String stacRepresentation; - const StacRenderer({super.key, required this.stacRepresentation}); - - @override - Widget build(BuildContext context) { - // return SingleChildScrollView( - // child: SelectableText(stacRepresentation), - // ); - return stac.StacApp( - title: 'Component Preview', - homeBuilder: (context) => Material( - color: Colors.transparent, - child: stac.Stac.fromJson(jsonDecode(stacRepresentation), context), - ), - ); - } -} - -class AIGenerateUIButton extends ConsumerWidget { - const AIGenerateUIButton({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - return FilledButton.tonalIcon( - style: FilledButton.styleFrom( - padding: kPh12, - minimumSize: const Size(44, 44), - ), - onPressed: () { - final model = ref.watch(selectedRequestModelProvider - .select((value) => value?.httpResponseModel)); - showCustomDialog( - context, - GenerateUIDialog(content: model?.formattedBody ?? ""), - ); - }, - icon: Icon( - Icons.generating_tokens, - ), - label: const SizedBox( - child: Text( - kLabelGenerateUI, - ), - ), - ); - } -} diff --git a/lib/screens/home_page/editor_pane/agentic_ui_features/ai_ui_designer/ai_ui_designer_agentcalls.dart b/lib/screens/home_page/editor_pane/agentic_ui_features/ai_ui_designer/ai_ui_designer_agentcalls.dart new file mode 100644 index 00000000..b1f1fffa --- /dev/null +++ b/lib/screens/home_page/editor_pane/agentic_ui_features/ai_ui_designer/ai_ui_designer_agentcalls.dart @@ -0,0 +1,66 @@ +import 'package:apidash/services/agentic_services/agent_caller.dart'; +import 'package:apidash/services/agentic_services/agents/agents.dart'; +import 'package:apidash_core/apidash_core.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +Future generateSDUICodeFromResponse({ + required WidgetRef ref, + required String apiResponse, +}) async { + final step1Res = await Future.wait([ + APIDashAgentCaller.instance.call( + ResponseSemanticAnalyser(), + ref: ref, + input: AgentInputs(query: apiResponse), + ), + APIDashAgentCaller.instance.call( + IntermediateRepresentationGen(), + ref: ref, + input: AgentInputs(variables: { + 'VAR_API_RESPONSE': apiResponse, + }), + ), + ]); + final SA = step1Res[0]?['SEMANTIC_ANALYSIS']; + final IR = step1Res[1]?['INTERMEDIATE_REPRESENTATION']; + + if (SA == null || IR == null) { + return null; + } + + print("Semantic Analysis: $SA"); + print("Intermediate Representation: $IR"); + + final sduiCode = await APIDashAgentCaller.instance.call( + StacGenBot(), + ref: ref, + input: AgentInputs(variables: { + 'VAR_RAW_API_RESPONSE': apiResponse, + 'VAR_INTERMEDIATE_REPR': IR, + 'VAR_SEMANTIC_ANALYSIS': SA, + }), + ); + final stacCode = sduiCode?['STAC']?.toString(); + if (stacCode == null) { + return null; + } + + return sduiCode['STAC'].toString(); +} + +Future modifySDUICodeUsingPrompt({ + required WidgetRef ref, + required String generatedSDUI, + required String modificationRequest, +}) async { + final res = await APIDashAgentCaller.instance.call( + StacModifierBot(), + ref: ref, + input: AgentInputs(variables: { + 'VAR_CODE': generatedSDUI, + 'VAR_CLIENT_REQUEST': modificationRequest, + }), + ); + final SDUI = res?['STAC']; + return SDUI; +} diff --git a/lib/screens/home_page/editor_pane/agentic_ui_features/ai_ui_designer/framework_selector.dart b/lib/screens/home_page/editor_pane/agentic_ui_features/ai_ui_designer/framework_selector.dart new file mode 100644 index 00000000..2a800328 --- /dev/null +++ b/lib/screens/home_page/editor_pane/agentic_ui_features/ai_ui_designer/framework_selector.dart @@ -0,0 +1,119 @@ +import 'package:apidash/consts.dart'; +import 'package:apidash_design_system/apidash_design_system.dart'; +import 'package:flutter/material.dart'; + +class FrameWorkSelectorPage extends StatefulWidget { + final String content; + final Function(String, String) onNext; + const FrameWorkSelectorPage( + {super.key, required this.content, required this.onNext}); + + @override + State createState() => _FrameWorkSelectorPageState(); +} + +class _FrameWorkSelectorPageState extends State { + String? selectedFramework; + TextEditingController controller = TextEditingController(); + + @override + void initState() { + controller.text = widget.content; + super.initState(); + } + + @override + Widget build(BuildContext context) { + final textContainerdecoration = BoxDecoration( + color: Color.alphaBlend( + (Theme.of(context).brightness == Brightness.dark + ? Theme.of(context).colorScheme.onPrimaryContainer + : Theme.of(context).colorScheme.primaryContainer) + .withValues(alpha: kForegroundOpacity), + Theme.of(context).colorScheme.surface), + border: Border.all( + color: Theme.of(context).colorScheme.surfaceContainerHighest), + borderRadius: kBorderRadius8, + ); + + return Container( + width: MediaQuery.of(context).size.width * 0.6, // Large dialog + padding: EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Container( + width: double.maxFinite, + padding: kP8, + decoration: textContainerdecoration, + child: SingleChildScrollView( + child: TextField( + controller: controller, + maxLines: null, + style: kCodeStyle, + ), + ), + ), + ), + kVSpacer20, + // Text( + // "Select Framework", + // style: TextStyle( + // color: Colors.white, + // fontSize: 18, + // fontWeight: FontWeight.bold, + // ), + // ), + // SizedBox(height: 10), + // DropdownButtonFormField( + // dropdownColor: Color(0xFF2D2D2D), + // decoration: InputDecoration( + // filled: true, + // fillColor: Color(0xFF2D2D2D), + // border: OutlineInputBorder( + // borderRadius: BorderRadius.circular(8.0), + // ), + // ), + // value: selectedFramework, + // items: ["Flutter", "ReactJS"].map((String value) { + // return DropdownMenuItem( + // value: value, + // child: Text( + // value, + // style: TextStyle(color: Colors.white), + // ), + // ); + // }).toList(), + // onChanged: (newValue) { + // selectedFramework = newValue; + // setState(() {}); + // }, + // ), + // kVSpacer20, + Align( + alignment: Alignment.centerRight, + child: FilledButton.tonalIcon( + style: FilledButton.styleFrom( + padding: kPh12, + minimumSize: const Size(44, 44), + ), + onPressed: () { + widget.onNext(controller.value.text, "FLUTTER"); + }, + icon: Icon( + Icons.generating_tokens, + ), + label: const SizedBox( + child: Text( + kLabelGenerateUI, + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/screens/home_page/editor_pane/agentic_ui_features/ai_ui_designer/generate_ui_dialog.dart b/lib/screens/home_page/editor_pane/agentic_ui_features/ai_ui_designer/generate_ui_dialog.dart new file mode 100644 index 00000000..b458608b --- /dev/null +++ b/lib/screens/home_page/editor_pane/agentic_ui_features/ai_ui_designer/generate_ui_dialog.dart @@ -0,0 +1,181 @@ +import 'package:apidash/consts.dart'; +import 'package:apidash/providers/collection_providers.dart'; +import 'package:apidash/widgets/widget_sending.dart'; +import 'package:apidash_design_system/apidash_design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'ai_ui_designer_agentcalls.dart'; +import 'framework_selector.dart'; +import 'sdui_preview.dart'; + +void showCustomDialog(BuildContext context, Widget dialogContent) { + showDialog( + context: context, + builder: (BuildContext context) { + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.0), + ), + child: dialogContent, + ); + }, + ); +} + +class GenerateUIDialog extends ConsumerStatefulWidget { + final String content; + const GenerateUIDialog({ + super.key, + required this.content, + }); + + @override + ConsumerState createState() => _GenerateUIDialogState(); +} + +class _GenerateUIDialogState extends ConsumerState { + int index = 0; + TextEditingController controller = TextEditingController(); + + String generatedSDUI = '{}'; + + Future generateSDUICode(String apiResponse) async { + try { + setState(() { + index = 1; //Induce Loading + }); + final res = await generateSDUICodeFromResponse( + ref: ref, + apiResponse: apiResponse, + ); + if (res == null) { + setState(() { + index = 0; + }); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text( + "Preview Generation Failed!", + style: TextStyle(color: Colors.white), + ), + backgroundColor: Colors.redAccent, + )); + return null; + } + return res; + } catch (e) { + String errMsg = 'Unexpected Error Occured'; + if (e.toString().contains('NO_DEFAULT_LLM')) { + errMsg = "Please Select Default AI Model in Settings"; + } + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text( + errMsg, + style: TextStyle(color: Colors.white), + ), + backgroundColor: Colors.redAccent, + )); + Navigator.pop(context); + } + } + + Future modifySDUICode(String modificationRequest) async { + setState(() { + index = 1; //Induce Loading + }); + final res = await modifySDUICodeUsingPrompt( + generatedSDUI: generatedSDUI, + ref: ref, + modificationRequest: modificationRequest, + ); + if (res == null) { + setState(() { + index = 2; + }); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text( + "Modification Request Failed!", + style: TextStyle(color: Colors.white), + ), + backgroundColor: Colors.redAccent, + )); + return; + } + setState(() { + generatedSDUI = res; + index = 2; + }); + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + if (index == 0) + FrameWorkSelectorPage( + content: widget.content, + onNext: (apiResponse, targetLanguage) async { + print("Generating SDUI Code"); + final sdui = await generateSDUICode(apiResponse); + if (sdui == null) return; + setState(() { + index = 2; + generatedSDUI = sdui; + }); + }, + ), + if (index == 1) + SizedBox( + width: MediaQuery.of(context).size.width * 0.6, + child: Center( + child: Padding( + padding: const EdgeInsets.only(top: 40.0), + child: Container( + height: 500, + child: SendingWidget( + startSendingTime: DateTime.now(), + showTimeElapsed: false, + ), + ), + ), + ), + ), + if (index == 2) + SDUIPreviewPage( + key: ValueKey(generatedSDUI.hashCode), + onModificationRequestMade: modifySDUICode, + sduiCode: generatedSDUI, + ) + ], + ); + } +} + +class AIGenerateUIButton extends ConsumerWidget { + const AIGenerateUIButton({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return FilledButton.tonalIcon( + style: FilledButton.styleFrom( + padding: kPh12, + minimumSize: const Size(44, 44), + ), + onPressed: () { + final model = ref.watch(selectedRequestModelProvider + .select((value) => value?.httpResponseModel)); + showCustomDialog( + context, + GenerateUIDialog(content: model?.formattedBody ?? ""), + ); + }, + icon: Icon( + Icons.generating_tokens, + ), + label: const SizedBox( + child: Text( + kLabelGenerateUI, + ), + ), + ); + } +} diff --git a/lib/screens/home_page/editor_pane/agentic_ui_features/ai_ui_designer/sdui_preview.dart b/lib/screens/home_page/editor_pane/agentic_ui_features/ai_ui_designer/sdui_preview.dart new file mode 100644 index 00000000..c4a764c1 --- /dev/null +++ b/lib/screens/home_page/editor_pane/agentic_ui_features/ai_ui_designer/sdui_preview.dart @@ -0,0 +1,181 @@ +import 'package:apidash/screens/home_page/editor_pane/agentic_ui_features/ai_ui_designer/sdui_renderer.dart'; +import 'package:apidash/services/agentic_services/agent_caller.dart'; +import 'package:apidash/services/agentic_services/agents/stac2flutter.dart'; +import 'package:apidash_core/apidash_core.dart'; +import 'package:apidash_design_system/tokens/measurements.dart'; +import 'package:apidash_design_system/widgets/textfield_outlined.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class SDUIPreviewPage extends ConsumerStatefulWidget { + final String sduiCode; + final Function(String) onModificationRequestMade; + const SDUIPreviewPage({ + super.key, + required this.onModificationRequestMade, + required this.sduiCode, + }); + + @override + ConsumerState createState() => _SDUIPreviewPageState(); +} + +class _SDUIPreviewPageState extends ConsumerState { + bool exportingCode = false; + String modificationRequest = ""; + + exportCode() async { + setState(() { + exportingCode = true; + }); + final ans = await APIDashAgentCaller.instance.call( + StacToFlutterBot(), + ref: ref, + input: AgentInputs( + variables: {'VAR_CODE': widget.sduiCode}, + ), + ); + final exportedCode = ans?['CODE']; + + if (exportedCode == null) { + setState(() { + exportingCode = false; + }); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text( + "Export Failed", + style: TextStyle(color: Colors.white), + ), + backgroundColor: Colors.redAccent, + )); + print("exportCode: Failed; ABORTING"); + return; + } + + Clipboard.setData(ClipboardData(text: ans['CODE'])); + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text("Copied to clipboard!"))); + setState(() { + exportingCode = false; + }); + } + + @override + Widget build(BuildContext context) { + return Container( + width: MediaQuery.of(context).size.width * 0.6, // Large dialog + padding: EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Generated Component", + style: TextStyle( + fontSize: 20, + ), + ), + kVSpacer20, + Expanded( + child: Center( + child: Container( + padding: EdgeInsets.all(20), + decoration: BoxDecoration( + border: Border.all(color: Colors.white), + ), + child: StacRenderer( + stacRepresentation: widget.sduiCode, + onError: () { + Future.delayed(Duration(milliseconds: 200), () { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text( + "Failed to Display Preview", + style: TextStyle(color: Colors.white), + ), + backgroundColor: Colors.redAccent, + )); + }); + }, + ), + ), + ), + ), + kVSpacer20, + if (!exportingCode) ...[ + Container( + width: double.infinity, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + ), + child: ADOutlinedTextField( + hintText: 'Any Modifications?', + onChanged: (z) { + setState(() { + modificationRequest = z; + }); + }, + maxLines: 3, // Makes the text box taller + ), + ), + kVSpacer20, + ], + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Align( + alignment: Alignment.centerRight, + child: (exportingCode) + ? Container( + child: CircularProgressIndicator( + strokeWidth: 1, + ), + margin: EdgeInsets.only(right: 10), + ) + : FilledButton.tonalIcon( + style: FilledButton.styleFrom( + padding: kPh12, + minimumSize: const Size(44, 44), + ), + onPressed: exportCode, + icon: Icon( + Icons.download, + ), + label: const SizedBox( + child: Text( + "Export Code", + ), + ), + ), + ), + kHSpacer10, + if (!exportingCode) + Align( + alignment: Alignment.centerRight, + child: FilledButton.tonalIcon( + style: FilledButton.styleFrom( + padding: kPh12, + minimumSize: const Size(44, 44), + ), + onPressed: () { + if (modificationRequest.isNotEmpty) { + widget.onModificationRequestMade(modificationRequest); + } + }, + icon: Icon( + Icons.generating_tokens, + ), + label: const SizedBox( + child: Text( + "Make Modifications", + ), + ), + ), + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/screens/home_page/editor_pane/agentic_ui_features/ai_ui_designer/sdui_renderer.dart b/lib/screens/home_page/editor_pane/agentic_ui_features/ai_ui_designer/sdui_renderer.dart new file mode 100644 index 00000000..a9128475 --- /dev/null +++ b/lib/screens/home_page/editor_pane/agentic_ui_features/ai_ui_designer/sdui_renderer.dart @@ -0,0 +1,45 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:stac/stac.dart' as stac; + +class StacRenderer extends StatefulWidget { + final String stacRepresentation; + final VoidCallback onError; + const StacRenderer( + {super.key, required this.stacRepresentation, required this.onError}); + + @override + State createState() => _StacRendererState(); +} + +class _StacRendererState extends State { + Map? sduiCode; + + @override + void initState() { + super.initState(); + try { + sduiCode = jsonDecode(widget.stacRepresentation); + } catch (e) { + widget.onError(); + } + } + + @override + Widget build(BuildContext context) { + // return SingleChildScrollView( + // child: SelectableText(sduiCode?.toString() ?? ""), + // ); + if (sduiCode == null || sduiCode!.isEmpty) { + return Container(); + } + return stac.StacApp( + title: 'Component Preview', + homeBuilder: (context) => Material( + color: Colors.transparent, + child: stac.Stac.fromJson(sduiCode!.cast(), context), + ), + ); + } +} diff --git a/lib/screens/home_page/editor_pane/agentic_ui_elements/tool_generation/ai_toolgen_widgets.dart b/lib/screens/home_page/editor_pane/agentic_ui_features/tool_generation/ai_toolgen_widgets.dart similarity index 98% rename from lib/screens/home_page/editor_pane/agentic_ui_elements/tool_generation/ai_toolgen_widgets.dart rename to lib/screens/home_page/editor_pane/agentic_ui_features/tool_generation/ai_toolgen_widgets.dart index 9c064c58..48a9e867 100644 --- a/lib/screens/home_page/editor_pane/agentic_ui_elements/tool_generation/ai_toolgen_widgets.dart +++ b/lib/screens/home_page/editor_pane/agentic_ui_features/tool_generation/ai_toolgen_widgets.dart @@ -1,13 +1,10 @@ import 'dart:convert'; - import 'package:apidash/apitoolgen/request_consolidator.dart'; import 'package:apidash/apitoolgen/tool_templates.dart'; -import 'package:apidash/consts.dart'; import 'package:apidash/screens/common_widgets/ai/ai_model_selector_button.dart'; import 'package:apidash/services/agentic_services/agent_caller.dart'; import 'package:apidash/services/agentic_services/agents/apitool_bodygen.dart'; import 'package:apidash/services/agentic_services/agents/apitool_funcgen.dart'; -import 'package:apidash/screens/home_page/editor_pane/agentic_ui_elements/ai_ui_designer/ai_ui_desginer_widgets.dart'; import 'package:apidash/widgets/button_copy.dart'; import 'package:apidash/widgets/previewer_code.dart'; import 'package:apidash/widgets/widget_sending.dart'; @@ -16,9 +13,8 @@ import 'package:apidash_design_system/tokens/tokens.dart'; import 'package:apidash_design_system/widgets/popup_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:genai/agentic_engine/blueprint.dart'; - import '../../../../../providers/providers.dart'; +import '../ai_ui_designer/generate_ui_dialog.dart'; class GenerateToolDialog extends ConsumerStatefulWidget { final APIDashRequestDescription requestDesc; diff --git a/lib/services/agentic_services/agent_caller.dart b/lib/services/agentic_services/agent_caller.dart index c74169bd..4286b664 100644 --- a/lib/services/agentic_services/agent_caller.dart +++ b/lib/services/agentic_services/agent_caller.dart @@ -1,8 +1,6 @@ import 'package:apidash/providers/providers.dart'; +import 'package:apidash_core/apidash_core.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:genai/agentic_engine/agent_service.dart'; -import 'package:genai/agentic_engine/blueprint.dart'; -import 'package:genai/genai.dart'; class APIDashAgentCaller { static APIDashAgentCaller instance = APIDashAgentCaller(); diff --git a/lib/widgets/response_body_success.dart b/lib/widgets/response_body_success.dart index b31a0971..b4c1b006 100644 --- a/lib/widgets/response_body_success.dart +++ b/lib/widgets/response_body_success.dart @@ -1,5 +1,5 @@ -import 'package:apidash/screens/home_page/editor_pane/agentic_ui_elements/tool_generation/ai_toolgen_widgets.dart'; -import 'package:apidash/screens/home_page/editor_pane/agentic_ui_elements/ai_ui_designer/ai_ui_desginer_widgets.dart'; +import 'package:apidash/screens/home_page/editor_pane/agentic_ui_features/ai_ui_designer/generate_ui_dialog.dart'; +import 'package:apidash/screens/home_page/editor_pane/agentic_ui_features/tool_generation/ai_toolgen_widgets.dart'; import 'package:apidash_core/apidash_core.dart'; import 'package:apidash_design_system/apidash_design_system.dart'; import 'package:flutter/foundation.dart'; diff --git a/packages/genai/lib/agentic_engine/agentic_engine.dart b/packages/genai/lib/agentic_engine/agentic_engine.dart new file mode 100644 index 00000000..abc52f3b --- /dev/null +++ b/packages/genai/lib/agentic_engine/agentic_engine.dart @@ -0,0 +1,2 @@ +export 'agent_service.dart'; +export 'blueprint.dart'; diff --git a/packages/genai/lib/genai.dart b/packages/genai/lib/genai.dart index 7d58af8d..9332e59a 100644 --- a/packages/genai/lib/genai.dart +++ b/packages/genai/lib/genai.dart @@ -2,3 +2,4 @@ export 'models/models.dart'; export 'interface/interface.dart'; export 'utils/utils.dart'; export 'widgets/widgets.dart'; +export 'agentic_engine/agentic_engine.dart';