import "package:file_picker/file_picker.dart"; import "package:flutter/foundation.dart"; import "../../src/project_store.dart"; import "../../src/session/session_types.dart"; import "chat_provider.dart"; import "../models/attachment.dart"; import "projects_provider.dart"; import "session_provider.dart"; import "settings_provider.dart"; class HomeCoordinator extends ChangeNotifier { HomeCoordinator(this._projects, this._session, this._chat, this._settings); final ProjectsProvider _projects; final SessionProvider _session; final ChatProvider _chat; final SettingsProvider _settings; String? _error; String? get error => _error; void clearError() { _error = null; notifyListeners(); } void _setError(String msg) { _error = msg; notifyListeners(); } Future pickProjectDirectory() async { try { final selectedDirectory = await FilePicker.platform.getDirectoryPath( dialogTitle: "Select project directory", ); if (selectedDirectory == null) return; final project = await _projects.addProject(selectedDirectory); if (project == null) { _setError("The selected folder could not be added as a project."); return; } _projects.selectProject(project.id); _session.clearCurrentSession(workingDirectory: project.workingDirectory); _chat.clearConversation(); await _settings.setActiveProject(project.workingDirectory); } catch (e, st) { print("Project directory picker failed: $e"); print(st); _setError(e.toString()); } } void createNewChat() { final selectedProject = _projects.selectedProject; if (selectedProject == null) { _setError("Choose a project first so the new chat has a working directory."); return; } // Don't create the session yet — that happens on first message send. // Just clear the current state so the UI shows a blank chat. _session.clearCurrentSession(workingDirectory: selectedProject.workingDirectory); _chat.clearConversation(); } Future selectProject(ProjectRecord project) async { _projects.selectProject(project.id); await _settings.setActiveProject(project.workingDirectory); if (_session.currentSession?.workingDirectory == project.workingDirectory) return; _session.clearCurrentSession(workingDirectory: project.workingDirectory); _settings.setThreadModel(null); _chat.clearConversation(); } Future openSession(SessionSummary session) async { // If a live runtime exists for this session, just switch focus to it // without reloading from disk — avoids disrupting an in-progress turn. if (_chat.isSessionRunning(session.id)) { _chat.activateSessionById(session.id); _session.setActiveSessionId(session.id); _projects.selectProjectByWorkingDirectory(session.workingDirectory); _settings.setThreadModel(session.model); return; } await _session.loadSession(session); final loaded = _session.currentSession; if (loaded != null) { _chat.activateSession(loaded); } _projects.selectProjectByWorkingDirectory(_session.activeWorkingDirectory); _settings.setThreadModel(_session.currentSession?.model); } Future sendMessage(String text, {List? attachments}) async { final hasAttachments = attachments != null && attachments.isNotEmpty; if (text.isEmpty && !hasAttachments) return; if (_session.currentSession == null) { final selectedProject = _projects.selectedProject; if (selectedProject == null) { _setError("Pick a project before starting a chat."); return; } await _session.createNewSession( workingDirectory: selectedProject.workingDirectory, name: "New Chat", model: _settings.settings.model, ); _settings.setThreadModel(_settings.settings.model); final newSession = _session.currentSession; if (newSession != null) { _chat.activateSession(newSession); } } try { await _chat.sendMessage(text, attachments: attachments); } catch (e, st) { print("Failed to send message: $e"); print(st); _setError(e.toString()); } finally { await _session.refreshSessions(); } } Future deleteSession(SessionSummary session) async { await _session.deleteSession(session); } }