146 lines
4.5 KiB
Dart
146 lines
4.5 KiB
Dart
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) {
|
|
_chat.onSessionNameChanged = (_, __) => _session.refreshSessions();
|
|
}
|
|
|
|
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<void> 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<void> 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<void> 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);
|
|
_chat.markSessionRead(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);
|
|
_chat.markSessionRead(session.id);
|
|
}
|
|
_projects.selectProjectByWorkingDirectory(_session.activeWorkingDirectory);
|
|
_settings.setThreadModel(_session.currentSession?.model);
|
|
}
|
|
|
|
Future<void> sendMessage(String text, {List<Attachment>? 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);
|
|
_chat.markSessionRead(newSession.id);
|
|
}
|
|
}
|
|
|
|
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<void> deleteSession(SessionSummary session) async {
|
|
await _session.deleteSession(session);
|
|
}
|
|
|
|
}
|