From bea78c50e6724eda030e9db5504075626fd99051 Mon Sep 17 00:00:00 2001 From: hunteraraujo Date: Sun, 10 Sep 2023 13:50:29 -0700 Subject: [PATCH 01/11] Add graph view dependency --- frontend/pubspec.lock | 8 ++++++++ frontend/pubspec.yaml | 1 + 2 files changed, 9 insertions(+) diff --git a/frontend/pubspec.lock b/frontend/pubspec.lock index 85634a73..9ef72f46 100644 --- a/frontend/pubspec.lock +++ b/frontend/pubspec.lock @@ -104,6 +104,14 @@ packages: description: flutter source: sdk version: "0.0.0" + graphview: + dependency: "direct main" + description: + name: graphview + sha256: bdba183583b23c30c71edea09ad5f0beef612572d3e39e855467a925bd08392f + url: "https://pub.dev" + source: hosted + version: "1.2.0" highlight: dependency: transitive description: diff --git a/frontend/pubspec.yaml b/frontend/pubspec.yaml index f546fb0c..18dbc62d 100644 --- a/frontend/pubspec.yaml +++ b/frontend/pubspec.yaml @@ -39,6 +39,7 @@ dependencies: provider: ^6.0.5 http: ^1.1.0 shared_preferences: ^2.2.1 + graphview: ^1.2.0 dev_dependencies: flutter_test: From e13f7ca757d39ff2d4b58e62e2ccae9092ba4465 Mon Sep 17 00:00:00 2001 From: hunteraraujo Date: Sun, 10 Sep 2023 13:51:36 -0700 Subject: [PATCH 02/11] Add Ground data model for skill tree The Ground data model stores evaluation information for each skill node. It includes: - The answer to be evaluated - A list of terms that should be contained in the answer - A list of terms that should not be contained in the answer - A list of associated files - A map for additional evaluation criteria --- frontend/lib/models/skill_tree/ground.dart | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 frontend/lib/models/skill_tree/ground.dart diff --git a/frontend/lib/models/skill_tree/ground.dart b/frontend/lib/models/skill_tree/ground.dart new file mode 100644 index 00000000..2cc61c0d --- /dev/null +++ b/frontend/lib/models/skill_tree/ground.dart @@ -0,0 +1,25 @@ +class Ground { + final String answer; + final List shouldContain; + final List shouldNotContain; + final List files; + final Map eval; + + Ground({ + required this.answer, + required this.shouldContain, + required this.shouldNotContain, + required this.files, + required this.eval, + }); + + factory Ground.fromJson(Map json) { + return Ground( + answer: json['answer'], + shouldContain: List.from(json['should_contain']), + shouldNotContain: List.from(json['should_not_contain']), + files: List.from(json['files']), + eval: json['eval'], + ); + } +} From 5ed6a08c228d3dfe4ca875c1e1a493f8e65891bd Mon Sep 17 00:00:00 2001 From: hunteraraujo Date: Sun, 10 Sep 2023 13:52:02 -0700 Subject: [PATCH 03/11] Add Info data model for skill tree The Info data model holds metadata about a skill node. It includes: - The difficulty level of the skill node - A description of the skill node - A list of potential side effects related to the skill node --- frontend/lib/models/skill_tree/info.dart | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 frontend/lib/models/skill_tree/info.dart diff --git a/frontend/lib/models/skill_tree/info.dart b/frontend/lib/models/skill_tree/info.dart new file mode 100644 index 00000000..c5e61212 --- /dev/null +++ b/frontend/lib/models/skill_tree/info.dart @@ -0,0 +1,19 @@ +class Info { + final String difficulty; + final String description; + final List sideEffects; + + Info({ + required this.difficulty, + required this.description, + required this.sideEffects, + }); + + factory Info.fromJson(Map json) { + return Info( + difficulty: json['difficulty'], + description: json['description'], + sideEffects: List.from(json['side_effects']), + ); + } +} From 5726613dfb367a7c8153ade66542fe2266de603c Mon Sep 17 00:00:00 2001 From: hunteraraujo Date: Sun, 10 Sep 2023 13:56:59 -0700 Subject: [PATCH 04/11] Add SkillNodeData data model for skill tree The SkillNodeData model aggregates various data related to a skill node. It includes: - Node name - Node category - Associated task - Dependencies - Cutoff value - Ground object for evaluation details - Info object for metadata --- .../models/skill_tree/skill_node_data.dart | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 frontend/lib/models/skill_tree/skill_node_data.dart diff --git a/frontend/lib/models/skill_tree/skill_node_data.dart b/frontend/lib/models/skill_tree/skill_node_data.dart new file mode 100644 index 00000000..876bcc94 --- /dev/null +++ b/frontend/lib/models/skill_tree/skill_node_data.dart @@ -0,0 +1,34 @@ +import 'package:auto_gpt_flutter_client/models/skill_tree/ground.dart'; +import 'package:auto_gpt_flutter_client/models/skill_tree/info.dart'; + +class SkillNodeData { + final String name; + final List category; + final String task; + final List dependencies; + final int cutoff; + final Ground ground; + final Info info; + + SkillNodeData({ + required this.name, + required this.category, + required this.task, + required this.dependencies, + required this.cutoff, + required this.ground, + required this.info, + }); + + factory SkillNodeData.fromJson(Map json) { + return SkillNodeData( + name: json['name'], + category: List.from(json['category']), + task: json['task'], + dependencies: List.from(json['dependencies']), + cutoff: json['cutoff'], + ground: Ground.fromJson(json['ground']), + info: Info.fromJson(json['info']), + ); + } +} From e16e48f893f0a99ea611b56317739ab2d159a633 Mon Sep 17 00:00:00 2001 From: hunteraraujo Date: Sun, 10 Sep 2023 13:57:25 -0700 Subject: [PATCH 05/11] Add SkillTreeEdge data model for skill tree The SkillTreeEdge model represents the relationship between different skill nodes. It includes: - Edge ID - Source node ID - Destination node ID - Arrows property to indicate directionality --- .../models/skill_tree/skill_tree_edge.dart | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 frontend/lib/models/skill_tree/skill_tree_edge.dart diff --git a/frontend/lib/models/skill_tree/skill_tree_edge.dart b/frontend/lib/models/skill_tree/skill_tree_edge.dart new file mode 100644 index 00000000..4b7abd50 --- /dev/null +++ b/frontend/lib/models/skill_tree/skill_tree_edge.dart @@ -0,0 +1,23 @@ +class SkillTreeEdge { + final String id; + final String from; + final String to; + final String arrows; + + SkillTreeEdge({ + required this.id, + required this.from, + required this.to, + required this.arrows, + }); + + // Optionally, add a factory constructor to initialize from JSON + factory SkillTreeEdge.fromJson(Map json) { + return SkillTreeEdge( + id: json['id'], + from: json['from'], + to: json['to'], + arrows: json['arrows'], + ); + } +} From a6b791c4f0f3667d14ac3f68f2e96be131bea700 Mon Sep 17 00:00:00 2001 From: hunteraraujo Date: Sun, 10 Sep 2023 13:58:02 -0700 Subject: [PATCH 06/11] Update SkillTreeNode data model for skill tree The SkillTreeNode model represents a single node in the skill tree. It includes: - Node ID - Node color --- .../lib/models/skill_tree/skill_tree_node.dart | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 frontend/lib/models/skill_tree/skill_tree_node.dart diff --git a/frontend/lib/models/skill_tree/skill_tree_node.dart b/frontend/lib/models/skill_tree/skill_tree_node.dart new file mode 100644 index 00000000..6b94995c --- /dev/null +++ b/frontend/lib/models/skill_tree/skill_tree_node.dart @@ -0,0 +1,18 @@ +import 'package:auto_gpt_flutter_client/models/skill_tree/skill_node_data.dart'; + +// TODO: Update this with actual data +class SkillTreeNode { + final String color; + final int id; + + // final SkillNodeData data; + + SkillTreeNode({required this.color, required this.id}); + + factory SkillTreeNode.fromJson(Map json) { + return SkillTreeNode( + color: json['color'], + id: json['id'], + ); + } +} From d6b0894c6b1a2dbedb1d0a0bec71b0f211a27975 Mon Sep 17 00:00:00 2001 From: hunteraraujo Date: Sun, 10 Sep 2023 14:28:17 -0700 Subject: [PATCH 07/11] Add SkillTreeViewModel for managing skill tree state The SkillTreeViewModel class serves as the view model for the skill tree and extends Flutter's ChangeNotifier for state management. Features include: - Storing and managing the list of SkillTreeNodes and SkillTreeEdges. - Managing the state of the selected node. - Initializing the skill tree with predefined nodes and edges. - Methods for toggling node selection, allowing for only a single node to be selected at any given time. The view model utilizes the GraphView package for visualization and layout. --- .../lib/viewmodels/skill_tree_viewmodel.dart | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 frontend/lib/viewmodels/skill_tree_viewmodel.dart diff --git a/frontend/lib/viewmodels/skill_tree_viewmodel.dart b/frontend/lib/viewmodels/skill_tree_viewmodel.dart new file mode 100644 index 00000000..3e964dcf --- /dev/null +++ b/frontend/lib/viewmodels/skill_tree_viewmodel.dart @@ -0,0 +1,76 @@ +import 'package:auto_gpt_flutter_client/models/skill_tree/skill_tree_edge.dart'; +import 'package:auto_gpt_flutter_client/models/skill_tree/skill_tree_node.dart'; +import 'package:flutter/foundation.dart'; +import 'package:graphview/GraphView.dart'; + +class SkillTreeViewModel extends ChangeNotifier { + List _skillTreeNodes = []; + List _skillTreeEdges = []; + SkillTreeNode? _selectedNode; + + SkillTreeNode? get selectedNode => _selectedNode; + + final Graph graph = Graph()..isTree = true; + BuchheimWalkerConfiguration builder = BuchheimWalkerConfiguration(); + + void initializeSkillTree() { + _skillTreeNodes = []; + _skillTreeEdges = []; + + // Add nodes to _skillTreeNodes + _skillTreeNodes.addAll([ + SkillTreeNode(color: 'red', id: 1), + SkillTreeNode(color: 'blue', id: 2), + SkillTreeNode(color: 'green', id: 3), + SkillTreeNode(color: 'yellow', id: 4), + SkillTreeNode(color: 'orange', id: 5), + SkillTreeNode(color: 'purple', id: 6), + SkillTreeNode(color: 'brown', id: 7), + SkillTreeNode(color: 'pink', id: 8), + SkillTreeNode(color: 'grey', id: 9), + SkillTreeNode(color: 'cyan', id: 10), + SkillTreeNode(color: 'magenta', id: 11), + SkillTreeNode(color: 'lime', id: 12) + ]); + + // Add edges to _skillTreeEdges + _skillTreeEdges.addAll([ + SkillTreeEdge(id: '1_to_2', from: '1', to: '2', arrows: 'to'), + SkillTreeEdge(id: '1_to_3', from: '1', to: '3', arrows: 'to'), + SkillTreeEdge(id: '1_to_4', from: '1', to: '4', arrows: 'to'), + SkillTreeEdge(id: '2_to_5', from: '2', to: '5', arrows: 'to'), + SkillTreeEdge(id: '2_to_6', from: '2', to: '6', arrows: 'to'), + SkillTreeEdge(id: '6_to_7', from: '6', to: '7', arrows: 'to'), + SkillTreeEdge(id: '6_to_8', from: '6', to: '8', arrows: 'to'), + SkillTreeEdge(id: '4_to_9', from: '4', to: '9', arrows: 'to'), + SkillTreeEdge(id: '4_to_10', from: '4', to: '10', arrows: 'to'), + SkillTreeEdge(id: '4_to_11', from: '4', to: '11', arrows: 'to'), + SkillTreeEdge(id: '11_to_12', from: '11', to: '12', arrows: 'to') + ]); + + builder + ..siblingSeparation = (100) + ..levelSeparation = (150) + ..subtreeSeparation = (150) + ..orientation = (BuchheimWalkerConfiguration.ORIENTATION_LEFT_RIGHT); + + notifyListeners(); + } + + void toggleNodeSelection(int nodeId) { + if (_selectedNode?.id == nodeId) { + // Unselect the node if it's already selected + _selectedNode = null; + } else { + // Select the new node + _selectedNode = _skillTreeNodes.firstWhere((node) => node.id == nodeId); + } + notifyListeners(); + } + + // Getter to expose nodes for the View + List get skillTreeNodes => _skillTreeNodes; + + // Getter to expose edges for the View + List get skillTreeEdges => _skillTreeEdges; +} From 90e73b83a1c63ad4b29c3c2c36dca6515b99ce0d Mon Sep 17 00:00:00 2001 From: hunteraraujo Date: Sun, 10 Sep 2023 14:33:42 -0700 Subject: [PATCH 08/11] Implement TreeNodeView with selection functionality This commit adds the TreeNodeView class, a StatelessWidget responsible for rendering individual nodes in the skill tree. Features: - Displays the node ID in the view. - Uses the Provider package to interact with the SkillTreeViewModel. - Includes an onTap method to toggle node selection state. - Updates the UI to reflect the selected state by changing the background color. The TreeNodeView is designed to work in conjunction with SkillTreeViewModel to manage node selection states. --- .../lib/views/skill_tree/tree_node_view.dart | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 frontend/lib/views/skill_tree/tree_node_view.dart diff --git a/frontend/lib/views/skill_tree/tree_node_view.dart b/frontend/lib/views/skill_tree/tree_node_view.dart new file mode 100644 index 00000000..9f32582d --- /dev/null +++ b/frontend/lib/views/skill_tree/tree_node_view.dart @@ -0,0 +1,32 @@ +import 'package:auto_gpt_flutter_client/viewmodels/skill_tree_viewmodel.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class TreeNodeView extends StatelessWidget { + final int nodeId; + final bool selected; + + TreeNodeView({required this.nodeId, this.selected = false}); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: () { + print('Node $nodeId clicked'); + Provider.of(context, listen: false) + .toggleNodeSelection(nodeId); + }, + child: Container( + padding: EdgeInsets.all(16), + decoration: BoxDecoration( + color: selected ? Colors.red : Colors.white, + borderRadius: BorderRadius.circular(4), + boxShadow: [ + BoxShadow(color: Colors.red, spreadRadius: 1), + ], + ), + child: Text('Node $nodeId'), + ), + ); + } +} From 70a05ec82a4d49276f0e2618b62f12f7f66fb3ab Mon Sep 17 00:00:00 2001 From: hunteraraujo Date: Sun, 10 Sep 2023 14:42:15 -0700 Subject: [PATCH 09/11] Replace Placeholder SkillTreeView with Full Implementation This commit replaces the placeholder implementation of the SkillTreeView class with a complete, functional version. Features: - Initializes the skill tree from the SkillTreeViewModel during `initState`. - Dynamically creates Node and Edge objects for GraphView based on ViewModel data. - Utilizes the TreeNodeView to render individual nodes. - Integrates node selection functionality from the ViewModel. - Adds InteractiveViewer for zoom and pan capabilities. The new SkillTreeView is designed to work closely with SkillTreeViewModel to manage and display the skill tree. --- .../lib/views/skill_tree/skill_tree_view.dart | 71 ++++++++++++++++--- 1 file changed, 60 insertions(+), 11 deletions(-) diff --git a/frontend/lib/views/skill_tree/skill_tree_view.dart b/frontend/lib/views/skill_tree/skill_tree_view.dart index 8e2aee7e..bca252e3 100644 --- a/frontend/lib/views/skill_tree/skill_tree_view.dart +++ b/frontend/lib/views/skill_tree/skill_tree_view.dart @@ -1,20 +1,69 @@ +import 'package:auto_gpt_flutter_client/viewmodels/skill_tree_viewmodel.dart'; +import 'package:auto_gpt_flutter_client/views/skill_tree/tree_node_view.dart'; import 'package:flutter/material.dart'; +import 'package:graphview/GraphView.dart'; -class SkillTreeView extends StatelessWidget { - const SkillTreeView({super.key}); +class SkillTreeView extends StatefulWidget { + final SkillTreeViewModel viewModel; + + const SkillTreeView({Key? key, required this.viewModel}) : super(key: key); + + @override + _TreeViewPageState createState() => _TreeViewPageState(); +} + +class _TreeViewPageState extends State { + @override + void initState() { + super.initState(); + + widget.viewModel.initializeSkillTree(); + + // Create Node and Edge objects for GraphView + final Map nodeMap = {}; + for (var skillTreeNode in widget.viewModel.skillTreeNodes) { + final node = Node.Id(skillTreeNode.id); + widget.viewModel.graph.addNode(node); + nodeMap[skillTreeNode.id] = node; + } + + for (var skillTreeEdge in widget.viewModel.skillTreeEdges) { + final fromNode = nodeMap[int.parse(skillTreeEdge.from)]; + final toNode = nodeMap[int.parse(skillTreeEdge.to)]; + widget.viewModel.graph.addEdge(fromNode!, toNode!); + } + } @override Widget build(BuildContext context) { - return Container( - color: Colors.blue[100], // Background color - child: const Center( - child: Text( - 'SkillTreeView', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, + return Scaffold( + body: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: InteractiveViewer( + constrained: false, + boundaryMargin: EdgeInsets.all(100), + minScale: 0.01, + maxScale: 5.6, + child: GraphView( + graph: widget.viewModel.graph, + algorithm: BuchheimWalkerAlgorithm(widget.viewModel.builder, + TreeEdgeRenderer(widget.viewModel.builder)), + paint: Paint() + ..color = Colors.green + ..strokeWidth = 1 + ..style = PaintingStyle.stroke, + builder: (Node node) { + int nodeId = node.key?.value as int; + return TreeNodeView( + nodeId: nodeId, + selected: nodeId == widget.viewModel.selectedNode?.id); + }, + ), + ), ), - ), + ], ), ); } From 60264d68b509bb4df6bbf2cfdf1b8aa8db368ae1 Mon Sep 17 00:00:00 2001 From: hunteraraujo Date: Sun, 10 Sep 2023 14:45:21 -0700 Subject: [PATCH 10/11] Integrate SkillTreeViewModel into MainLayout --- frontend/lib/views/main_layout.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/lib/views/main_layout.dart b/frontend/lib/views/main_layout.dart index 96a8a5c5..44808e1e 100644 --- a/frontend/lib/views/main_layout.dart +++ b/frontend/lib/views/main_layout.dart @@ -1,3 +1,4 @@ +import 'package:auto_gpt_flutter_client/viewmodels/skill_tree_viewmodel.dart'; import 'package:auto_gpt_flutter_client/viewmodels/task_viewmodel.dart'; import 'package:auto_gpt_flutter_client/viewmodels/chat_viewmodel.dart'; import 'package:auto_gpt_flutter_client/views/side_bar/side_bar_view.dart'; @@ -23,6 +24,9 @@ class MainLayout extends StatelessWidget { // Access the ChatViewModel from the context final chatViewModel = Provider.of(context); + // Access the ChatViewModel from the context + final skillTreeViewModel = Provider.of(context); + // Check the screen width and return the appropriate layout if (width > 800) { // For larger screens, return a side-by-side layout @@ -36,7 +40,8 @@ class MainLayout extends StatelessWidget { return SizedBox( width: 280, child: TaskView(viewModel: taskViewModel)); } else { - return const Expanded(child: SkillTreeView()); + return Expanded( + child: SkillTreeView(viewModel: skillTreeViewModel)); } }, ), From 19db097709a879f028433226e289d1808ac4b255 Mon Sep 17 00:00:00 2001 From: hunteraraujo Date: Sun, 10 Sep 2023 14:45:37 -0700 Subject: [PATCH 11/11] ChangeNotifierProvider for SkillTreeViewModel --- frontend/lib/main.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/lib/main.dart b/frontend/lib/main.dart index 20bea239..f9a03fc4 100644 --- a/frontend/lib/main.dart +++ b/frontend/lib/main.dart @@ -1,4 +1,5 @@ import 'package:auto_gpt_flutter_client/viewmodels/api_settings_viewmodel.dart'; +import 'package:auto_gpt_flutter_client/viewmodels/skill_tree_viewmodel.dart'; import 'package:flutter/material.dart'; import 'views/main_layout.dart'; import 'package:provider/provider.dart'; @@ -55,6 +56,9 @@ class MyApp extends StatelessWidget { create: (context) => ChatViewModel(chatService)), ChangeNotifierProvider( create: (context) => TaskViewModel(taskService)), + ChangeNotifierProvider( + create: (context) => SkillTreeViewModel(), + ), ], child: MainLayout(), ),