From 0aabfba0fbcb25ad6182a45b35aa015b8dcae858 Mon Sep 17 00:00:00 2001 From: Manar Elhabbal Date: Mon, 20 Apr 2026 17:25:24 +0200 Subject: [PATCH 1/3] enhance editor code to be like the real IDE --- lib/widgets/editor_code.dart | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/widgets/editor_code.dart b/lib/widgets/editor_code.dart index 1e52ad9a1a..6272cc8ab0 100644 --- a/lib/widgets/editor_code.dart +++ b/lib/widgets/editor_code.dart @@ -36,11 +36,14 @@ class CodeEditor extends StatelessWidget { smartQuotesType: SmartQuotesType.enabled, background: Theme.of(context).colorScheme.surfaceContainerLowest, gutterStyle: GutterStyle( - width: 0, // TODO: Fix numbers size - margin: 2, - textAlign: TextAlign.left, + margin: 8, + textAlign: TextAlign.right, showFoldingHandles: false, - showLineNumbers: false, + showLineNumbers: true, + textStyle: kCodeStyle.copyWith( + fontSize: Theme.of(context).textTheme.labelSmall?.fontSize, + color: Theme.of(context).colorScheme.outline.withOpacity(0.6), + ), ), cursorColor: Theme.of(context).colorScheme.primary, controller: controller, From 5d82f77660a63c9e089dc16e7910a94f170ab15e Mon Sep 17 00:00:00 2001 From: Manar Elhabbal Date: Tue, 21 Apr 2026 00:25:44 +0200 Subject: [PATCH 2/3] feat:add model done --- lib/screens/common_widgets/ai/ai.dart | 1 + .../ai/ai_model_selector_dialog.dart | 261 ++++++++++-------- .../ai/dialog_add_ai_model.dart | 10 +- 3 files changed, 151 insertions(+), 121 deletions(-) diff --git a/lib/screens/common_widgets/ai/ai.dart b/lib/screens/common_widgets/ai/ai.dart index 20b1371b4d..7a1b9ec2cc 100644 --- a/lib/screens/common_widgets/ai/ai.dart +++ b/lib/screens/common_widgets/ai/ai.dart @@ -1,3 +1,4 @@ export 'ai_model_selector_button.dart'; export 'ai_model_selector_dialog.dart'; export 'ai_model_selector.dart'; +export 'dialog_add_ai_model.dart'; diff --git a/lib/screens/common_widgets/ai/ai_model_selector_dialog.dart b/lib/screens/common_widgets/ai/ai_model_selector_dialog.dart index b2cddfd52b..97aa7ab213 100644 --- a/lib/screens/common_widgets/ai/ai_model_selector_dialog.dart +++ b/lib/screens/common_widgets/ai/ai_model_selector_dialog.dart @@ -1,4 +1,4 @@ -// import 'package:apidash/providers/providers.dart'; +import 'ai.dart'; import 'package:apidash/widgets/widgets.dart'; import 'package:apidash/consts.dart'; import 'package:apidash_core/apidash_core.dart'; @@ -16,7 +16,8 @@ class AIModelSelectorDialog extends ConsumerStatefulWidget { } class _AIModelSelectorDialogState extends ConsumerState { - late final Future aM; + AvailableModels? availableModels; + bool isLoading = true; ModelAPIProvider? selectedProvider; AIRequestModel? newAIRequestModel; @@ -27,154 +28,163 @@ class _AIModelSelectorDialogState extends ConsumerState { if (selectedProvider != null && widget.aiRequestModel?.model != null) { newAIRequestModel = widget.aiRequestModel?.copyWith(); } - aM = ModelManager.fetchAvailableModels(); + ModelManager.fetchAvailableModels().then((value) { + if (mounted) { + setState(() { + availableModels = value; + isLoading = false; + }); + } + }); } @override Widget build(BuildContext context) { - // ref.watch(aiApiCredentialProvider); + if (isLoading) { + return const Center(child: CircularProgressIndicator()); + } + if (availableModels == null) { + return const Center(child: Text("Error loading models")); + } + + final data = availableModels!; + final mappedData = data.map; final width = MediaQuery.of(context).size.width * 0.8; - return FutureBuilder( - future: aM, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done && - snapshot.hasData && - snapshot.data != null) { - final data = snapshot.data!; - final mappedData = data.map; - if (context.isMediumWindow) { - return Container( - padding: kP20, - width: width, + + if (context.isMediumWindow) { + return Container( + padding: kP20, + width: width, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ElevatedButton( + onPressed: () async { + setState(() { + isLoading = true; + }); + final models = await ModelManager.fetchAvailableModels(); + if (mounted) { + setState(() { + availableModels = models; + isLoading = false; + }); + } + }, + child: Text(kLabelUpdateModels), + ), + kVSpacer10, + Row( + children: [ + Text(kLabelSelectModelProvider), + kHSpacer20, + Expanded( + child: ADDropdownButton( + onChanged: (x) { + setState(() { + selectedProvider = x; + newAIRequestModel = + mappedData[selectedProvider]?.toAiRequestModel(); + }); + }, + value: selectedProvider, + values: data.modelProviders.map( + (e) => (e.providerId!, e.providerName), + ), + ), + ), + ], + ), + kVSpacer10, + _buildModelSelector(mappedData[selectedProvider]), + ], + ), + ); + } + + return Container( + padding: kP20, + width: width, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Flexible( + flex: 1, + child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ElevatedButton( - onPressed: null, - // TODO: Add update model logic - //() async { - // await LLMManager.fetchAvailableLLMs(); - // setState(() {}); - //}, + onPressed: () async { + setState(() { + isLoading = true; + }); + final models = await ModelManager.fetchAvailableModels(); + if (mounted) { + setState(() { + availableModels = models; + isLoading = false; + }); + } + }, child: Text(kLabelUpdateModels), ), - kVSpacer10, - Row( - children: [ - Text(kLabelSelectModelProvider), - kHSpacer20, - Expanded( - child: ADDropdownButton( - onChanged: (x) { - setState(() { - selectedProvider = x; - newAIRequestModel = mappedData[selectedProvider] - ?.toAiRequestModel(); - }); - }, - value: selectedProvider, - values: data.modelProviders.map( - (e) => (e.providerId!, e.providerName), - ), - ), - ), - ], + SizedBox(height: 20), + ...data.modelProviders.map( + (x) => ListTile( + title: Text(x.providerName ?? ""), + trailing: selectedProvider != x.providerId + ? null + : const CircleAvatar( + radius: 5, + backgroundColor: Colors.green, + ), + onTap: () { + setState(() { + selectedProvider = x.providerId; + newAIRequestModel = + mappedData[selectedProvider]?.toAiRequestModel(); + }); + }, + ), ), - kVSpacer10, - _buildModelSelector(mappedData[selectedProvider]), ], ), - ); - } - - return Container( - padding: kP20, - width: width, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Flexible( - flex: 1, - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ElevatedButton( - onPressed: null, - // TODO: Add update model logic - //() async { - // await LLMManager.fetchAvailableLLMs(); - // setState(() {}); - //}, - child: Text(kLabelUpdateModels), - ), - SizedBox(height: 20), - ...data.modelProviders.map( - (x) => ListTile( - title: Text(x.providerName ?? ""), - trailing: selectedProvider != x.providerId - ? null - : CircleAvatar( - radius: 5, - backgroundColor: Colors.green, - ), - onTap: () { - setState(() { - selectedProvider = x.providerId; - newAIRequestModel = mappedData[selectedProvider] - ?.toAiRequestModel(); - }); - }, - ), - ), - ], - ), - ), - ), - SizedBox(width: 40), - Flexible( - flex: 3, - child: _buildModelSelector(mappedData[selectedProvider]), - ), - ], ), - ); - } - return Center(child: CircularProgressIndicator()); - }, + ), + const SizedBox(width: 40), + Flexible( + flex: 3, + child: _buildModelSelector(mappedData[selectedProvider]), + ), + ], + ), ); } - _buildModelSelector(AIModelProvider? aiModelProvider) { + Widget _buildModelSelector(AIModelProvider? aiModelProvider) { if (aiModelProvider == null) { - return Center(child: Text(kLabelSelectAIProvider)); + return const Center(child: Text(kLabelSelectAIProvider)); } - // final currentCredential = - // ref.watch(aiApiCredentialProvider)[aiModelProvider.providerId!] ?? ""; return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [ Text( aiModelProvider.providerName ?? "", - style: TextStyle(fontSize: 28), + style: const TextStyle(fontSize: 28), ), - SizedBox(height: 20), + const SizedBox(height: 20), if (aiModelProvider.providerId != ModelAPIProvider.ollama) ...[ Text(kLabelApiKeyCredential), kVSpacer8, BoundedTextField( onChanged: (x) { - // ref.read(aiApiCredentialProvider.notifier).state = { - // ...ref.read(aiApiCredentialProvider), - // aiModelProvider.providerId!: x - // }; setState(() { newAIRequestModel = newAIRequestModel?.copyWith(apiKey: x); }); }, value: newAIRequestModel?.apiKey ?? "", - // value: currentCredential, ), kVSpacer10, ], @@ -194,8 +204,27 @@ class _AIModelSelectorDialogState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(kLabelModels), - // IconButton( - // onPressed: () => addNewModel(context), icon: Icon(Icons.add)) + IconButton( + onPressed: () async { + final newModel = await addNewModel(context); + if (!mounted) return; + if (newModel != null && availableModels != null) { + setState(() { + final providerId = selectedProvider; + final providers = availableModels!.modelProviders.map((p) { + if (p.providerId == providerId) { + return p.copyWith( + models: [...(p.models ?? []), newModel]); + } + return p; + }).toList(); + availableModels = + availableModels!.copyWith(modelProviders: providers); + }); + } + }, + icon: const Icon(Icons.add), + ) ], ), kVSpacer8, @@ -218,7 +247,7 @@ class _AIModelSelectorDialogState extends ConsumerState { mainAxisSize: MainAxisSize.min, children: [ if (newAIRequestModel?.model == x.id) - CircleAvatar( + const CircleAvatar( radius: 5, backgroundColor: Colors.green, ), diff --git a/lib/screens/common_widgets/ai/dialog_add_ai_model.dart b/lib/screens/common_widgets/ai/dialog_add_ai_model.dart index d51feb7961..25c77bc645 100644 --- a/lib/screens/common_widgets/ai/dialog_add_ai_model.dart +++ b/lib/screens/common_widgets/ai/dialog_add_ai_model.dart @@ -1,11 +1,12 @@ +import 'package:apidash_core/apidash_core.dart' as ref; import 'package:apidash_design_system/apidash_design_system.dart'; import 'package:apidash/consts.dart'; import 'package:flutter/material.dart'; -Future addNewModel(BuildContext context) async { +Future addNewModel(BuildContext context) async { TextEditingController iC = TextEditingController(); TextEditingController nC = TextEditingController(); - final z = await showDialog( + final z = await showDialog>( context: context, builder: (context) { return AlertDialog( @@ -36,7 +37,6 @@ Future addNewModel(BuildContext context) async { ); iC.dispose(); nC.dispose(); - if (z == null) return; - // TODO: Add logic to add a new model - // setState(() {}); + if (z == null || z.length < 2) return null; + return ref.Model(id: z[0], name: z[1]); } From 62753e749f756aacc828958658d31100a0da9d9d Mon Sep 17 00:00:00 2001 From: Manar Elhabbal Date: Tue, 21 Apr 2026 01:47:35 +0200 Subject: [PATCH 3/3] Revert changes to lib/widgets/editor_code.dart --- lib/widgets/editor_code.dart | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/widgets/editor_code.dart b/lib/widgets/editor_code.dart index 6272cc8ab0..1e52ad9a1a 100644 --- a/lib/widgets/editor_code.dart +++ b/lib/widgets/editor_code.dart @@ -36,14 +36,11 @@ class CodeEditor extends StatelessWidget { smartQuotesType: SmartQuotesType.enabled, background: Theme.of(context).colorScheme.surfaceContainerLowest, gutterStyle: GutterStyle( - margin: 8, - textAlign: TextAlign.right, + width: 0, // TODO: Fix numbers size + margin: 2, + textAlign: TextAlign.left, showFoldingHandles: false, - showLineNumbers: true, - textStyle: kCodeStyle.copyWith( - fontSize: Theme.of(context).textTheme.labelSmall?.fontSize, - color: Theme.of(context).colorScheme.outline.withOpacity(0.6), - ), + showLineNumbers: false, ), cursorColor: Theme.of(context).colorScheme.primary, controller: controller,