import "package:go_router/go_router.dart"; import "package:provider/provider.dart"; import "package:shadcn_flutter/shadcn_flutter.dart"; import "../../../src/project_store.dart"; import "../../providers/chat_provider.dart"; import "../../providers/home_coordinator.dart"; import "../../providers/projects_provider.dart"; import "../../widgets/agents/agents_pane.dart"; import "../../widgets/chat/chat_box.dart"; import "../../widgets/chat/chat_view.dart"; import "../../widgets/common/footer_bar.dart"; import "../../widgets/sidebar/sidebar.dart"; class NewHomeScreen extends StatefulWidget { const NewHomeScreen({super.key}); @override State createState() => _NewHomeScreenState(); } class _NewHomeScreenState extends State { final ScrollController _chatScrollController = ScrollController(); @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { context.read().addListener(_onCoordinatorChanged); }); } @override void dispose() { context.read().removeListener(_onCoordinatorChanged); _chatScrollController.dispose(); super.dispose(); } void _onCoordinatorChanged() { final coordinator = context.read(); final err = coordinator.error; if (err != null) { coordinator.clearError(); _showError(err); } } Future _showError(String message) { return showDialog( context: context, builder: (dialogContext) => AlertDialog( title: const Text("Heads up"), content: Text(message), actions: [ Button.primary( onPressed: () => Navigator.of(dialogContext).pop(), child: const Text("OK"), ), ], ), ); } @override Widget build(BuildContext context) { return Scaffold( child: Column( children: [ Expanded( child: Row( children: [ Sidebar(), Gap(1), VerticalDivider(), Expanded( child: Stack( children: [ _ChatArea(scrollController: _chatScrollController), Positioned( top: 0, bottom: 0, right: 0, width: 12, child: FullHeightScrollbar(controller: _chatScrollController), ), ], ), ), AgentsPane(), ], ), ), FooterBar(), ], ), ); } } class _ChatArea extends StatelessWidget { final ScrollController scrollController; const _ChatArea({required this.scrollController}); @override Widget build(BuildContext context) { final chatProvider = context.watch(); return Container( alignment: Alignment.center, padding: const EdgeInsets.all(16), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 600), child: Column( children: [ Expanded( child: chatProvider.messages.isEmpty ? _EmptyChatState() : ChatView(scrollController: scrollController), ), ChatBox(), ], ), ), ); } } class _EmptyChatState extends StatelessWidget { const _EmptyChatState(); @override Widget build(BuildContext context) { final projectsProvider = context.watch(); final projects = projectsProvider.projects; final selected = projectsProvider.selectedProject; final coordinator = context.read(); return Center( child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(LucideIcons.messagesSquare, size: 28), const Gap(16), Text( "Ask the agency anything", style: const TextStyle(fontSize: 22, fontWeight: FontWeight.w700), textAlign: TextAlign.center, ), const Gap(8), Text( "Select a project and thread from the sidebar, or start a new chat.", textAlign: TextAlign.center, ).textSmall.muted, const Gap(24), Select( itemBuilder: (context, item) => Text(item.name), popup: SelectPopup.builder( searchPlaceholder: const Text("Search projects"), builder: (context, searchQuery) { final filtered = searchQuery == null || searchQuery.isEmpty ? projects : projects.where((p) => p.name.toLowerCase().contains(searchQuery.toLowerCase()) || p.workingDirectory.toLowerCase().contains(searchQuery.toLowerCase()) ).toList(); return SelectItemList( children: [ for (final project in filtered) SelectItemButton( value: project, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(project.name), Text(project.workingDirectory).textSmall.muted, ], ), ), ], ); }, ), onChanged: (project) { if (project != null) coordinator.selectProject(project); }, constraints: const BoxConstraints(minWidth: 240), value: selected, placeholder: const Text("Select a project"), ), ], ), ), ); } } abstract class HomeScreenRoute { static const path = '/'; static const name = 'home'; static GoRoute get route => GoRoute( path: path, name: name, builder: (context, state) => const NewHomeScreen(), ); }