diff --git a/src/app/editor/agent/agent_chat_widget.cc b/src/app/editor/agent/agent_chat_widget.cc index f1c17434..396185b8 100644 --- a/src/app/editor/agent/agent_chat_widget.cc +++ b/src/app/editor/agent/agent_chat_widget.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include "absl/status/status.h" #include "absl/strings/str_format.h" @@ -18,6 +19,7 @@ #include "app/editor/system/proposal_drawer.h" #include "app/editor/system/toast_manager.h" #include "app/gui/icons.h" +#include "app/core/project.h" #include "imgui/imgui.h" #include "imgui/misc/cpp/imgui_stdlib.h" @@ -1644,19 +1646,17 @@ void AgentChatWidget::RenderSystemPromptEditor() { ImGui::BeginChild("SystemPromptEditor", ImVec2(0, 0), false); // Toolbar - if (ImGui::Button(ICON_MD_FOLDER_OPEN " Load Default")) { - // Load system_prompt_v2.txt - std::string prompt_path = core::GetConfigDirectory() + "/../assets/agent/system_prompt_v2.txt"; - std::ifstream file(prompt_path); - if (file.is_open()) { + if (ImGui::Button(ICON_MD_FOLDER_OPEN " Load V1")) { + // Load embedded system_prompt.txt (v1) + std::string prompt_v1 = core::LoadFile("assets/agent/system_prompt.txt"); + if (!prompt_v1.empty()) { // Find or create system prompt tab bool found = false; for (auto& tab : open_files_) { if (tab.is_system_prompt) { - std::stringstream buffer; - buffer << file.rdbuf(); - tab.editor.SetText(buffer.str()); - tab.filepath = prompt_path; + tab.editor.SetText(prompt_v1); + tab.filepath = ""; // Not saved to disk + tab.filename = "system_prompt_v1.txt (built-in)"; found = true; break; } @@ -1664,39 +1664,80 @@ void AgentChatWidget::RenderSystemPromptEditor() { if (!found) { FileEditorTab tab; - tab.filename = "system_prompt_v2.txt"; - tab.filepath = prompt_path; + tab.filename = "system_prompt_v1.txt (built-in)"; + tab.filepath = ""; tab.is_system_prompt = true; tab.editor.SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus()); - std::stringstream buffer; - buffer << file.rdbuf(); - tab.editor.SetText(buffer.str()); + tab.editor.SetText(prompt_v1); open_files_.push_back(std::move(tab)); active_file_tab_ = static_cast(open_files_.size()) - 1; } if (toast_manager_) { - toast_manager_->Show("System prompt loaded", ToastType::kSuccess); + toast_manager_->Show("System prompt V1 loaded", ToastType::kSuccess); } } else if (toast_manager_) { - toast_manager_->Show("Could not load system prompt file", ToastType::kError); + toast_manager_->Show("Could not load system prompt V1", ToastType::kError); } } ImGui::SameLine(); - if (ImGui::Button(ICON_MD_SAVE " Save")) { - // Save the current system prompt + if (ImGui::Button(ICON_MD_FOLDER_OPEN " Load V2")) { + // Load embedded system_prompt_v2.txt + std::string prompt_v2 = core::LoadFile("assets/agent/system_prompt_v2.txt"); + if (!prompt_v2.empty()) { + // Find or create system prompt tab + bool found = false; + for (auto& tab : open_files_) { + if (tab.is_system_prompt) { + tab.editor.SetText(prompt_v2); + tab.filepath = ""; // Not saved to disk + tab.filename = "system_prompt_v2.txt (built-in)"; + found = true; + break; + } + } + + if (!found) { + FileEditorTab tab; + tab.filename = "system_prompt_v2.txt (built-in)"; + tab.filepath = ""; + tab.is_system_prompt = true; + tab.editor.SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus()); + tab.editor.SetText(prompt_v2); + open_files_.push_back(std::move(tab)); + active_file_tab_ = static_cast(open_files_.size()) - 1; + } + + if (toast_manager_) { + toast_manager_->Show("System prompt V2 loaded", ToastType::kSuccess); + } + } else if (toast_manager_) { + toast_manager_->Show("Could not load system prompt V2", ToastType::kError); + } + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_MD_SAVE " Save to Project")) { + // Save the current system prompt to project directory for (auto& tab : open_files_) { - if (tab.is_system_prompt && !tab.filepath.empty()) { - std::ofstream file(tab.filepath); - if (file.is_open()) { - file << tab.editor.GetText(); - tab.modified = false; - if (toast_manager_) { - toast_manager_->Show("System prompt saved", ToastType::kSuccess); + if (tab.is_system_prompt) { + auto save_path = core::FileDialogWrapper::ShowSaveFileDialog( + "custom_system_prompt", "txt"); + if (!save_path.empty()) { + std::ofstream file(save_path); + if (file.is_open()) { + file << tab.editor.GetText(); + tab.filepath = save_path; + tab.filename = core::GetFileName(save_path); + tab.modified = false; + if (toast_manager_) { + toast_manager_->Show(absl::StrFormat("System prompt saved to %s", save_path), + ToastType::kSuccess); + } + } else if (toast_manager_) { + toast_manager_->Show("Failed to save system prompt", ToastType::kError); } - } else if (toast_manager_) { - toast_manager_->Show("Failed to save system prompt", ToastType::kError); } break; } @@ -1705,10 +1746,56 @@ void AgentChatWidget::RenderSystemPromptEditor() { ImGui::SameLine(); if (ImGui::Button(ICON_MD_NOTE_ADD " Create New")) { - CreateNewFileInEditor("system_prompt_custom.txt"); - if (!open_files_.empty()) { - open_files_.back().is_system_prompt = true; - open_files_.back().editor.SetText("# Custom System Prompt\n\nEnter your custom system prompt here...\n"); + FileEditorTab tab; + tab.filename = "custom_system_prompt.txt (unsaved)"; + tab.filepath = ""; + tab.is_system_prompt = true; + tab.modified = true; + tab.editor.SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus()); + tab.editor.SetText("# Custom System Prompt\n\nEnter your custom system prompt here...\n"); + open_files_.push_back(std::move(tab)); + active_file_tab_ = static_cast(open_files_.size()) - 1; + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_MD_FOLDER_OPEN " Load Custom")) { + auto filepath = core::FileDialogWrapper::ShowOpenFileDialog(); + if (!filepath.empty()) { + std::ifstream file(filepath); + if (file.is_open()) { + bool found = false; + for (auto& tab : open_files_) { + if (tab.is_system_prompt) { + std::stringstream buffer; + buffer << file.rdbuf(); + tab.editor.SetText(buffer.str()); + tab.filepath = filepath; + tab.filename = core::GetFileName(filepath); + tab.modified = false; + found = true; + break; + } + } + + if (!found) { + FileEditorTab tab; + tab.filename = core::GetFileName(filepath); + tab.filepath = filepath; + tab.is_system_prompt = true; + tab.editor.SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus()); + std::stringstream buffer; + buffer << file.rdbuf(); + tab.editor.SetText(buffer.str()); + open_files_.push_back(std::move(tab)); + active_file_tab_ = static_cast(open_files_.size()) - 1; + } + + if (toast_manager_) { + toast_manager_->Show("Custom system prompt loaded", ToastType::kSuccess); + } + } else if (toast_manager_) { + toast_manager_->Show("Could not load file", ToastType::kError); + } } } @@ -1919,5 +2006,83 @@ void AgentChatWidget::CreateNewFileInEditor(const std::string& filename) { active_file_tab_ = static_cast(open_files_.size()) - 1; } +void AgentChatWidget::LoadAgentSettingsFromProject(const core::YazeProject& project) { + // Load AI provider settings from project + agent_config_.ai_provider = project.agent_settings.ai_provider; + agent_config_.ai_model = project.agent_settings.ai_model; + agent_config_.ollama_host = project.agent_settings.ollama_host; + agent_config_.gemini_api_key = project.agent_settings.gemini_api_key; + agent_config_.show_reasoning = project.agent_settings.show_reasoning; + agent_config_.verbose = project.agent_settings.verbose; + agent_config_.max_tool_iterations = project.agent_settings.max_tool_iterations; + agent_config_.max_retry_attempts = project.agent_settings.max_retry_attempts; + + // Copy to buffer for ImGui + strncpy(agent_config_.provider_buffer, agent_config_.ai_provider.c_str(), + sizeof(agent_config_.provider_buffer) - 1); + strncpy(agent_config_.model_buffer, agent_config_.ai_model.c_str(), + sizeof(agent_config_.model_buffer) - 1); + strncpy(agent_config_.ollama_host_buffer, agent_config_.ollama_host.c_str(), + sizeof(agent_config_.ollama_host_buffer) - 1); + strncpy(agent_config_.gemini_key_buffer, agent_config_.gemini_api_key.c_str(), + sizeof(agent_config_.gemini_key_buffer) - 1); + + // Load custom system prompt if specified + if (project.agent_settings.use_custom_prompt && + !project.agent_settings.custom_system_prompt.empty()) { + std::string prompt_path = project.GetAbsolutePath(project.agent_settings.custom_system_prompt); + std::ifstream file(prompt_path); + if (file.is_open()) { + // Load into system prompt tab + bool found = false; + for (auto& tab : open_files_) { + if (tab.is_system_prompt) { + std::stringstream buffer; + buffer << file.rdbuf(); + tab.editor.SetText(buffer.str()); + tab.filepath = prompt_path; + tab.filename = core::GetFileName(prompt_path); + found = true; + break; + } + } + + if (!found) { + FileEditorTab tab; + tab.filename = core::GetFileName(prompt_path); + tab.filepath = prompt_path; + tab.is_system_prompt = true; + tab.editor.SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus()); + std::stringstream buffer; + buffer << file.rdbuf(); + tab.editor.SetText(buffer.str()); + open_files_.push_back(std::move(tab)); + } + } + } +} + +void AgentChatWidget::SaveAgentSettingsToProject(core::YazeProject& project) { + // Save AI provider settings to project + project.agent_settings.ai_provider = agent_config_.ai_provider; + project.agent_settings.ai_model = agent_config_.ai_model; + project.agent_settings.ollama_host = agent_config_.ollama_host; + project.agent_settings.gemini_api_key = agent_config_.gemini_api_key; + project.agent_settings.show_reasoning = agent_config_.show_reasoning; + project.agent_settings.verbose = agent_config_.verbose; + project.agent_settings.max_tool_iterations = agent_config_.max_tool_iterations; + project.agent_settings.max_retry_attempts = agent_config_.max_retry_attempts; + + // Check if a custom system prompt is loaded + for (const auto& tab : open_files_) { + if (tab.is_system_prompt && !tab.filepath.empty()) { + project.agent_settings.custom_system_prompt = + project.GetRelativePath(tab.filepath); + project.agent_settings.use_custom_prompt = true; + break; + } + } +} + } // namespace editor } // namespace yaze diff --git a/src/app/editor/agent/agent_chat_widget.h b/src/app/editor/agent/agent_chat_widget.h index ea1bd406..c3cfc437 100644 --- a/src/app/editor/agent/agent_chat_widget.h +++ b/src/app/editor/agent/agent_chat_widget.h @@ -12,6 +12,7 @@ #include "absl/time/time.h" #include "app/gui/modules/text_editor.h" #include "cli/service/agent/conversational_agent_service.h" +#include "core/project.h" namespace yaze { @@ -176,6 +177,10 @@ public: // Agent configuration accessors const AgentConfigState& GetAgentConfig() const { return agent_config_; } void UpdateAgentConfig(const AgentConfigState& config); + + // Load agent settings from project + void LoadAgentSettingsFromProject(const core::YazeProject& project); + void SaveAgentSettingsToProject(core::YazeProject& project); // Collaboration history management (public so EditorManager can call them) void SwitchToSharedHistory(const std::string& session_id); diff --git a/src/app/editor/editor_manager.cc b/src/app/editor/editor_manager.cc index a519a912..dc1f5eb9 100644 --- a/src/app/editor/editor_manager.cc +++ b/src/app/editor/editor_manager.cc @@ -514,7 +514,7 @@ void EditorManager::Initialize(const std::string& filename) { context_.shortcut_manager.GetKeys("Open"), context_.shortcut_manager.GetCallback("Open")}, {absl::StrCat(ICON_MD_HISTORY, " Open Recent"), "", []() {}, - []() { return !manager.GetRecentFiles().empty(); }, recent_files}, + [&manager]() { return !manager.GetRecentFiles().empty(); }, recent_files}, {absl::StrCat(ICON_MD_FILE_DOWNLOAD, " Save"), context_.shortcut_manager.GetKeys("Save"), context_.shortcut_manager.GetCallback("Save")}, diff --git a/src/app/emu/emulator.cc b/src/app/emu/emulator.cc index 929dedd1..b7177b34 100644 --- a/src/app/emu/emulator.cc +++ b/src/app/emu/emulator.cc @@ -1,6 +1,7 @@ #include "app/emu/emulator.h" #include +#include #include #include "app/core/platform/file_dialog.h" diff --git a/src/cli/handlers/agent/tool_commands.cc b/src/cli/handlers/agent/tool_commands.cc index f0d2fccc..80642aa8 100644 --- a/src/cli/handlers/agent/tool_commands.cc +++ b/src/cli/handlers/agent/tool_commands.cc @@ -25,6 +25,7 @@ #include "cli/handlers/message.h" #include "cli/handlers/overworld_inspect.h" #include "cli/service/resources/resource_context_builder.h" +#include "nlohmann/json.hpp" #include "util/macro.h" ABSL_DECLARE_FLAG(std::string, rom); @@ -1353,22 +1354,20 @@ absl::Status HandleOverworldGetEntranceCommand( static_cast(entrance_id))); if (format == "json") { - std::cout << "{\n"; - std::cout << absl::StrFormat(" \"entrance_id\": %d,\n", entrance.entrance_id); - std::cout << absl::StrFormat(" \"map\": \"0x%02X\",\n", entrance.map_id); - std::cout << absl::StrFormat(" \"world\": \"%s\",\n", overworld::WorldName(entrance.world)); - std::cout << absl::StrFormat(" \"position\": {\"x\": %d, \"y\": %d},\n", entrance.x, entrance.y); - std::cout << absl::StrFormat(" \"room_id\": \"0x%04X\",\n", entrance.room_id); - std::cout << absl::StrFormat(" \"door_type_1\": \"0x%04X\",\n", entrance.door_type_1); - std::cout << absl::StrFormat(" \"door_type_2\": \"0x%04X\",\n", entrance.door_type_2); - std::cout << absl::StrFormat(" \"is_hole\": %s", entrance.is_hole ? "true" : "false"); + nlohmann::json j; + j["entrance_id"] = entrance.entrance_id; + j["map"] = absl::StrFormat("0x%02X", entrance.map_id); + j["world"] = overworld::WorldName(entrance.world); + j["position"] = {{"x", entrance.x}, {"y", entrance.y}}; + j["area"] = {{"x", static_cast(entrance.area_x)}, {"y", static_cast(entrance.area_y)}}; + j["map_pos"] = absl::StrFormat("0x%04X", entrance.map_pos); + j["is_hole"] = entrance.is_hole; + if (entrance.entrance_name.has_value()) { - std::cout << absl::StrFormat(",\n \"entrance_name\": \"%s\"", *entrance.entrance_name); + j["entrance_name"] = *entrance.entrance_name; } - if (entrance.room_name.has_value()) { - std::cout << absl::StrFormat(",\n \"room_name\": \"%s\"", *entrance.room_name); - } - std::cout << "\n}\n"; + + std::cout << j.dump(2) << std::endl; } else { std::cout << absl::StrFormat("🚪 Entrance #%d\n", entrance.entrance_id); if (entrance.entrance_name.has_value()) { @@ -1377,13 +1376,10 @@ absl::Status HandleOverworldGetEntranceCommand( std::cout << absl::StrFormat("Map: 0x%02X (%s World)\n", entrance.map_id, overworld::WorldName(entrance.world)); std::cout << absl::StrFormat("Position: (%d, %d)\n", entrance.x, entrance.y); - std::cout << absl::StrFormat("→ Leads to Room 0x%04X", entrance.room_id); - if (entrance.room_name.has_value()) { - std::cout << " (" << *entrance.room_name << ")"; - } - std::cout << "\n"; - std::cout << absl::StrFormat("Door Types: 0x%04X / 0x%04X\n", - entrance.door_type_1, entrance.door_type_2); + std::cout << absl::StrFormat("Area: (%d, %d)\n", + static_cast(entrance.area_x), + static_cast(entrance.area_y)); + std::cout << absl::StrFormat("Map Pos: 0x%04X\n", entrance.map_pos); std::cout << absl::StrFormat("Is Hole: %s\n", entrance.is_hole ? "yes" : "no"); } diff --git a/src/cli/handlers/patch.cc b/src/cli/handlers/patch.cc index 113a7e8a..62f8cba5 100644 --- a/src/cli/handlers/patch.cc +++ b/src/cli/handlers/patch.cc @@ -1,3 +1,5 @@ +#include + #include "asar-dll-bindings/c/asar.h" #include "cli/z3ed.h" #include "cli/tui/asar_patch.h" diff --git a/src/cli/handlers/rom.cc b/src/cli/handlers/rom.cc index bf34afb8..4ebd650e 100644 --- a/src/cli/handlers/rom.cc +++ b/src/cli/handlers/rom.cc @@ -1,4 +1,7 @@ #include "cli/z3ed.h" + +#include + #include "absl/flags/flag.h" #include "absl/flags/declare.h" #include "absl/strings/str_format.h" diff --git a/src/cli/tui.cc b/src/cli/tui.cc index 8cad44ca..1efd60b4 100644 --- a/src/cli/tui.cc +++ b/src/cli/tui.cc @@ -1,5 +1,7 @@ #include "tui.h" +#include + #include #include #include