From c3f03472c18f79264426bec94e562930d331c9e2 Mon Sep 17 00:00:00 2001 From: scawful Date: Sun, 5 Oct 2025 14:54:19 -0400 Subject: [PATCH] refactor: Remove app_main.cc as the application entry point - Deleted app_main.cc to streamline the project structure, as it is no longer needed. - Updated CMakeLists.txt to remove references to the deleted app_main.cc file, ensuring a clean build configuration. --- src/CMakeLists.txt | 5 - src/app/editor/agent/agent_chat_widget.cc | 235 ++--- src/app/editor/agent/agent_editor.cc | 1015 ++++++++++++--------- src/app_main.cc | 89 -- src/cli/service/ai/prompt_builder.cc | 18 +- 5 files changed, 665 insertions(+), 697 deletions(-) delete mode 100644 src/app_main.cc diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d385b3f7..cd9ceeed 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -482,11 +482,6 @@ if (YAZE_BUILD_LIB) ./yaze.cc cli/service/gui/gui_automation_client.cc ) - - # Application main entry point (uses controller and full dependencies) - set(YAZE_APP_MAIN_SOURCES - ./app_main.cc - ) if(YAZE_USE_MODULAR_BUILD) # Aggregate modular libraries into an interface target for backward compatibility diff --git a/src/app/editor/agent/agent_chat_widget.cc b/src/app/editor/agent/agent_chat_widget.cc index f665e9c4..01034ff0 100644 --- a/src/app/editor/agent/agent_chat_widget.cc +++ b/src/app/editor/agent/agent_chat_widget.cc @@ -810,7 +810,7 @@ void AgentChatWidget::Draw() { // Connection status bar at top (taller for better visibility) ImDrawList* draw_list = ImGui::GetWindowDrawList(); ImVec2 bar_start = ImGui::GetCursorScreenPos(); - ImVec2 bar_size(ImGui::GetContentRegionAvail().x, 90); // Increased from 55 + ImVec2 bar_size(ImGui::GetContentRegionAvail().x, 60); // Increased from 55 // Gradient background ImU32 color_top = ImGui::GetColorU32(ImVec4(0.18f, 0.22f, 0.28f, 1.0f)); @@ -1046,8 +1046,17 @@ void AgentChatWidget::Draw() { ImVec2(4, 3)); // Compact padding // Removed RenderAgentConfigPanel - duplicates connection header - RenderZ3EDCommandPanel(); - RenderMultimodalPanel(); + if (ImGui::BeginTable("##commands_and_multimodal", 2, ImGuiTableFlags_BordersInnerV)) { + ImGui::TableSetupColumn("Commands", ImGuiTableColumnFlags_WidthFixed, 180); + ImGui::TableSetupColumn("Multimodal", ImGuiTableColumnFlags_WidthFixed, ImGui::GetContentRegionAvail().x - 180); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + RenderZ3EDCommandPanel(); + ImGui::TableSetColumnIndex(1); + RenderMultimodalPanel(); + ImGui::EndTable(); + } + RenderCollaborationPanel(); RenderRomSyncPanel(); RenderProposalManagerPanel(); @@ -1068,6 +1077,10 @@ void AgentChatWidget::Draw() { void AgentChatWidget::RenderCollaborationPanel() { ImGui::PushID("CollabPanel"); + // Tighter style for more content + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 3)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(3, 2)); + // Update reactive status color const bool connected = collaboration_state_.active; collaboration_status_color_ = connected ? ImVec4(0.133f, 0.545f, 0.133f, 1.0f) @@ -1075,13 +1088,10 @@ void AgentChatWidget::RenderCollaborationPanel() { // Always visible (no collapsing header) ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.12f, 0.14f, 0.18f, 0.95f)); - ImGui::BeginChild("CollabPanel", ImVec2(0, 200), true); + ImGui::BeginChild("CollabPanel", ImVec2(0, 140), true); // reduced height ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_PEOPLE " Collaboration"); - ImGui::Separator(); - - // Mode selector (compact inline) - ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), - ICON_MD_SETTINGS_ETHERNET " Mode:"); + ImGui::SameLine(); + ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_SETTINGS_ETHERNET " Mode:"); ImGui::SameLine(); ImGui::RadioButton(ICON_MD_FOLDER " Local##collab_mode_local", reinterpret_cast(&collaboration_state_.mode), @@ -1091,13 +1101,11 @@ void AgentChatWidget::RenderCollaborationPanel() { reinterpret_cast(&collaboration_state_.mode), static_cast(CollaborationMode::kNetwork)); - ImGui::Spacing(); - // Main content in table layout (fixed size to prevent auto-resize) if (ImGui::BeginTable("Collab_MainTable", 2, ImGuiTableFlags_BordersInnerV)) { - ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed, 180); + ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed, 150); ImGui::TableSetupColumn("Controls", ImGuiTableColumnFlags_WidthFixed, - ImGui::GetContentRegionAvail().x - 180); + ImGui::GetContentRegionAvail().x - 150); ImGui::TableNextRow(); // LEFT COLUMN: Session Details @@ -1106,10 +1114,9 @@ void AgentChatWidget::RenderCollaborationPanel() { ImGui::PushID("StatusColumn"); ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.15f, 0.2f, 0.18f, 0.4f)); - ImGui::BeginChild("Collab_SessionDetails", ImVec2(0, 80), true); + ImGui::BeginChild("Collab_SessionDetails", ImVec2(0, 60), true); // reduced height ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), - ICON_MD_INFO " Session Status:"); - ImGui::Spacing(); + ICON_MD_INFO " Session:"); if (connected) { ImGui::TextColored(collaboration_status_color_, ICON_MD_CHECK_CIRCLE " Connected"); @@ -1118,70 +1125,53 @@ void AgentChatWidget::RenderCollaborationPanel() { } if (collaboration_state_.mode == CollaborationMode::kNetwork) { - ImGui::Spacing(); ImGui::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f), ICON_MD_CLOUD " Server:"); - ImGui::TextWrapped("%s", collaboration_state_.server_url.c_str()); + ImGui::TextUnformatted(collaboration_state_.server_url.c_str()); } if (!collaboration_state_.session_name.empty()) { - ImGui::Spacing(); ImGui::TextColored(collaboration_status_color_, - ICON_MD_LABEL " Session:"); - ImGui::TextWrapped("%s", collaboration_state_.session_name.c_str()); + ICON_MD_LABEL " %s", collaboration_state_.session_name.c_str()); } if (!collaboration_state_.session_id.empty()) { - ImGui::Spacing(); ImGui::TextColored(collaboration_status_color_, - ICON_MD_KEY " Session Code:"); - ImGui::TextWrapped("%s", collaboration_state_.session_id.c_str()); + ICON_MD_KEY " %s", collaboration_state_.session_id.c_str()); + ImGui::SameLine(); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.4f, 0.6f, 0.6f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.416f, 0.353f, 0.804f, 1.0f)); - if (ImGui::Button(ICON_MD_CONTENT_COPY " Copy##copy_session_id")) { + if (ImGui::SmallButton(ICON_MD_CONTENT_COPY "##copy_session_id")) { ImGui::SetClipboardText(collaboration_state_.session_id.c_str()); if (toast_manager_) { - toast_manager_->Show("Session code copied!", ToastType::kSuccess, - 2.0f); + toast_manager_->Show("Session code copied!", ToastType::kSuccess, 2.0f); } } ImGui::PopStyleColor(2); } if (collaboration_state_.last_synced != absl::InfinitePast()) { - ImGui::Spacing(); ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f), - ICON_MD_ACCESS_TIME " Last sync:"); - ImGui::SameLine(); - ImGui::TextColored( - ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "%s", - absl::FormatTime("%H:%M:%S", collaboration_state_.last_synced, - absl::LocalTimeZone()) - .c_str()); + ICON_MD_ACCESS_TIME " %s", + absl::FormatTime("%H:%M:%S", collaboration_state_.last_synced, + absl::LocalTimeZone()).c_str()); } ImGui::EndChild(); ImGui::PopStyleColor(); - ImGui::Spacing(); - // Participants list below session details ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.12f, 0.16f, 0.14f, 0.4f)); ImGui::BeginChild("Collab_ParticipantsList", ImVec2(0, 0), true); - { - if (collaboration_state_.participants.empty()) { - ImGui::TextDisabled(ICON_MD_PEOPLE " No participants yet"); - } else { - ImGui::TextColored(collaboration_status_color_, - ICON_MD_PEOPLE " Participants (%zu):", - collaboration_state_.participants.size()); - ImGui::Separator(); - for (size_t i = 0; i < collaboration_state_.participants.size(); ++i) { - ImGui::PushID(static_cast(i)); - ImGui::BulletText(ICON_MD_PERSON " %s", - collaboration_state_.participants[i].c_str()); - ImGui::PopID(); - } + if (collaboration_state_.participants.empty()) { + ImGui::TextDisabled(ICON_MD_PEOPLE " No participants"); + } else { + ImGui::TextColored(collaboration_status_color_, + ICON_MD_PEOPLE " %zu", collaboration_state_.participants.size()); + for (size_t i = 0; i < collaboration_state_.participants.size(); ++i) { + ImGui::PushID(static_cast(i)); + ImGui::BulletText("%s", collaboration_state_.participants[i].c_str()); + ImGui::PopID(); } } ImGui::EndChild(); @@ -1196,8 +1186,6 @@ void AgentChatWidget::RenderCollaborationPanel() { ImGui::PushID("ControlsColumn"); ImGui::BeginChild("Collab_Controls", ImVec2(0, 0), false); - ImGui::Separator(); - const bool can_host = static_cast(collaboration_callbacks_.host_session); const bool can_join = @@ -1209,67 +1197,52 @@ void AgentChatWidget::RenderCollaborationPanel() { // Network mode: Show server URL input with styling if (collaboration_state_.mode == CollaborationMode::kNetwork) { - ImGui::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f), - ICON_MD_CLOUD " Server URL:"); - ImGui::SetNextItemWidth(-80); - ImGui::InputText("##collab_server_url", server_url_buffer_, - IM_ARRAYSIZE(server_url_buffer_)); + ImGui::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f), ICON_MD_CLOUD); + ImGui::SameLine(); + ImGui::SetNextItemWidth(100); + ImGui::InputText("##collab_server_url", server_url_buffer_, IM_ARRAYSIZE(server_url_buffer_)); ImGui::SameLine(); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.5f, 0.7f, 0.8f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, - ImVec4(0.196f, 0.6f, 0.8f, 1.0f)); - if (ImGui::Button(ICON_MD_LINK "##connect_server_btn")) { + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.196f, 0.6f, 0.8f, 1.0f)); + if (ImGui::SmallButton(ICON_MD_LINK "##connect_server_btn")) { collaboration_state_.server_url = server_url_buffer_; if (toast_manager_) { - toast_manager_->Show("Connecting to server...", ToastType::kInfo, - 3.0f); + toast_manager_->Show("Connecting to server...", ToastType::kInfo, 3.0f); } } ImGui::PopStyleColor(2); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Connect to collaboration server"); } - ImGui::Separator(); } - ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), - ICON_MD_ADD_CIRCLE " Host New Session:"); - ImGui::SetNextItemWidth(-70); - ImGui::InputTextWithHint("##collab_session_name", "Enter session name...", - session_name_buffer_, - IM_ARRAYSIZE(session_name_buffer_)); + // Host session + ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_ADD_CIRCLE); ImGui::SameLine(); - if (!can_host) - ImGui::BeginDisabled(); + ImGui::SetNextItemWidth(100); + ImGui::InputTextWithHint("##collab_session_name", "Session name...", session_name_buffer_, IM_ARRAYSIZE(session_name_buffer_)); + ImGui::SameLine(); + if (!can_host) ImGui::BeginDisabled(); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.6f, 0.5f, 0.0f, 0.8f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, - ImVec4(1.0f, 0.843f, 0.0f, 1.0f)); - if (ImGui::Button(ICON_MD_ROCKET_LAUNCH "##host_session_btn")) { + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0.843f, 0.0f, 1.0f)); + if (ImGui::SmallButton(ICON_MD_ROCKET_LAUNCH "##host_session_btn")) { std::string name = session_name_buffer_; if (name.empty()) { if (toast_manager_) { - toast_manager_->Show("Enter a session name first", - ToastType::kWarning, 3.0f); + toast_manager_->Show("Enter a session name first", ToastType::kWarning, 3.0f); } } else { auto session_or = collaboration_callbacks_.host_session(name); if (session_or.ok()) { - ApplyCollaborationSession(session_or.value(), - /*update_action_timestamp=*/true); - std::snprintf(join_code_buffer_, sizeof(join_code_buffer_), "%s", - collaboration_state_.session_id.c_str()); + ApplyCollaborationSession(session_or.value(), /*update_action_timestamp=*/true); + std::snprintf(join_code_buffer_, sizeof(join_code_buffer_), "%s", collaboration_state_.session_id.c_str()); session_name_buffer_[0] = '\0'; if (toast_manager_) { - toast_manager_->Show( - absl::StrFormat("Hosting session %s", - collaboration_state_.session_id.c_str()), - ToastType::kSuccess, 3.5f); + toast_manager_->Show(absl::StrFormat("Hosting session %s", collaboration_state_.session_id.c_str()), ToastType::kSuccess, 3.5f); } MarkHistoryDirty(); } else if (toast_manager_) { - toast_manager_->Show(absl::StrFormat("Failed to host: %s", - session_or.status().message()), - ToastType::kError, 5.0f); + toast_manager_->Show(absl::StrFormat("Failed to host: %s", session_or.status().message()), ToastType::kError, 5.0f); } } } @@ -1283,44 +1256,32 @@ void AgentChatWidget::RenderCollaborationPanel() { ImGui::SetTooltip("Host a new collaboration session"); } - ImGui::Spacing(); - ImGui::TextColored(ImVec4(0.133f, 0.545f, 0.133f, 1.0f), - ICON_MD_LOGIN " Join Existing Session:"); - ImGui::SetNextItemWidth(-70); - ImGui::InputTextWithHint("##collab_join_code", "Enter session code...", - join_code_buffer_, - IM_ARRAYSIZE(join_code_buffer_)); + // Join session + ImGui::TextColored(ImVec4(0.133f, 0.545f, 0.133f, 1.0f), ICON_MD_LOGIN); ImGui::SameLine(); - if (!can_join) - ImGui::BeginDisabled(); + ImGui::SetNextItemWidth(100); + ImGui::InputTextWithHint("##collab_join_code", "Session code...", join_code_buffer_, IM_ARRAYSIZE(join_code_buffer_)); + ImGui::SameLine(); + if (!can_join) ImGui::BeginDisabled(); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.1f, 0.4f, 0.1f, 0.8f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, - ImVec4(0.133f, 0.545f, 0.133f, 1.0f)); - if (ImGui::Button(ICON_MD_MEETING_ROOM "##join_session_btn")) { + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.133f, 0.545f, 0.133f, 1.0f)); + if (ImGui::SmallButton(ICON_MD_MEETING_ROOM "##join_session_btn")) { std::string code = join_code_buffer_; if (code.empty()) { if (toast_manager_) { - toast_manager_->Show("Enter a session code first", - ToastType::kWarning, 3.0f); + toast_manager_->Show("Enter a session code first", ToastType::kWarning, 3.0f); } } else { auto session_or = collaboration_callbacks_.join_session(code); if (session_or.ok()) { - ApplyCollaborationSession(session_or.value(), - /*update_action_timestamp=*/true); - std::snprintf(join_code_buffer_, sizeof(join_code_buffer_), "%s", - collaboration_state_.session_id.c_str()); + ApplyCollaborationSession(session_or.value(), /*update_action_timestamp=*/true); + std::snprintf(join_code_buffer_, sizeof(join_code_buffer_), "%s", collaboration_state_.session_id.c_str()); if (toast_manager_) { - toast_manager_->Show( - absl::StrFormat("Joined session %s", - collaboration_state_.session_id.c_str()), - ToastType::kSuccess, 3.5f); + toast_manager_->Show(absl::StrFormat("Joined session %s", collaboration_state_.session_id.c_str()), ToastType::kSuccess, 3.5f); } MarkHistoryDirty(); } else if (toast_manager_) { - toast_manager_->Show(absl::StrFormat("Failed to join: %s", - session_or.status().message()), - ToastType::kError, 5.0f); + toast_manager_->Show(absl::StrFormat("Failed to join: %s", session_or.status().message()), ToastType::kError, 5.0f); } } } @@ -1334,17 +1295,12 @@ void AgentChatWidget::RenderCollaborationPanel() { ImGui::SetTooltip("Join an existing collaboration session"); } + // Leave/Refresh if (collaboration_state_.active) { - ImGui::Separator(); - ImGui::Spacing(); - - if (!can_leave) - ImGui::BeginDisabled(); + if (!can_leave) ImGui::BeginDisabled(); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.7f, 0.2f, 0.2f, 0.8f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, - ImVec4(0.863f, 0.078f, 0.235f, 1.0f)); - if (ImGui::Button(ICON_MD_LOGOUT " Leave Session##leave_session_btn", - ImVec2(-1, 0))) { + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.863f, 0.078f, 0.235f, 1.0f)); + if (ImGui::SmallButton(ICON_MD_LOGOUT "##leave_session_btn")) { absl::Status status = collaboration_callbacks_.leave_session ? collaboration_callbacks_.leave_session() : absl::OkStatus(); @@ -1352,42 +1308,30 @@ void AgentChatWidget::RenderCollaborationPanel() { collaboration_state_ = CollaborationState{}; join_code_buffer_[0] = '\0'; if (toast_manager_) { - toast_manager_->Show("Left collaborative session", ToastType::kInfo, - 3.0f); + toast_manager_->Show("Left collaborative session", ToastType::kInfo, 3.0f); } MarkHistoryDirty(); } else if (toast_manager_) { - toast_manager_->Show( - absl::StrFormat("Failed to leave: %s", status.message()), - ToastType::kError, 5.0f); + toast_manager_->Show(absl::StrFormat("Failed to leave: %s", status.message()), ToastType::kError, 5.0f); } } ImGui::PopStyleColor(2); - if (!can_leave) - ImGui::EndDisabled(); + if (!can_leave) ImGui::EndDisabled(); - ImGui::Spacing(); - ImGui::Separator(); - if (!can_refresh) - ImGui::BeginDisabled(); + ImGui::SameLine(); + if (!can_refresh) ImGui::BeginDisabled(); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.4f, 0.6f, 0.8f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, - ImVec4(0.416f, 0.353f, 0.804f, 1.0f)); - if (ImGui::Button(ICON_MD_REFRESH " Refresh Session##refresh_collab_btn", - ImVec2(-1, 0))) { + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.416f, 0.353f, 0.804f, 1.0f)); + if (ImGui::SmallButton(ICON_MD_REFRESH "##refresh_collab_btn")) { RefreshCollaboration(); } ImGui::PopStyleColor(2); - if (!can_refresh && - ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + if (!can_refresh && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { ImGui::SetTooltip("Provide refresh_session callback to enable"); } - if (!can_refresh) - ImGui::EndDisabled(); + if (!can_refresh) ImGui::EndDisabled(); } else { - ImGui::Spacing(); - ImGui::TextDisabled(ICON_MD_INFO - " Start or join a session to collaborate."); + ImGui::TextDisabled(ICON_MD_INFO " Start or join a session to collaborate."); } ImGui::EndChild(); // Collab_Controls @@ -1398,6 +1342,7 @@ void AgentChatWidget::RenderCollaborationPanel() { ImGui::EndChild(); ImGui::PopStyleColor(); + ImGui::PopStyleVar(2); ImGui::PopID(); // CollabPanel } @@ -1810,7 +1755,7 @@ void AgentChatWidget::RenderZ3EDCommandPanel() { void AgentChatWidget::RenderRomSyncPanel() { ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.18f, 0.14f, 0.12f, 1.0f)); - ImGui::BeginChild("RomSync", ImVec2(0, 200), true); + ImGui::BeginChild("RomSync", ImVec2(0, 100), true); ImGui::Text(ICON_MD_STORAGE " ROM State"); ImGui::Separator(); diff --git a/src/app/editor/agent/agent_editor.cc b/src/app/editor/agent/agent_editor.cc index 2dc115dd..cfebee09 100644 --- a/src/app/editor/agent/agent_editor.cc +++ b/src/app/editor/agent/agent_editor.cc @@ -4,16 +4,16 @@ #include #include -#include "absl/strings/str_format.h" #include "absl/strings/match.h" +#include "absl/strings/str_format.h" #include "absl/time/clock.h" +#include "app/core/platform/asset_loader.h" #include "app/editor/agent/agent_chat_widget.h" #include "app/editor/agent/agent_collaboration_coordinator.h" #include "app/editor/system/proposal_drawer.h" #include "app/editor/system/toast_manager.h" -#include "app/rom.h" #include "app/gui/icons.h" -#include "app/core/platform/asset_loader.h" +#include "app/rom.h" #include "util/file_util.h" #ifdef YAZE_WITH_GRPC @@ -33,12 +33,12 @@ AgentEditor::AgentEditor() { local_coordinator_ = std::make_unique(); prompt_editor_ = std::make_unique(); common_tiles_editor_ = std::make_unique(); - + // Initialize default configuration (legacy) current_config_.provider = "mock"; current_config_.show_reasoning = true; current_config_.max_tool_iterations = 4; - + // Initialize default bot profile current_profile_.name = "Default Z3ED Bot"; current_profile_.description = "Default bot for Zelda 3 ROM editing"; @@ -47,16 +47,18 @@ AgentEditor::AgentEditor() { current_profile_.max_tool_iterations = 4; current_profile_.max_retry_attempts = 3; current_profile_.tags = {"default", "z3ed"}; - + // Setup text editors - prompt_editor_->SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus()); + prompt_editor_->SetLanguageDefinition( + TextEditor::LanguageDefinition::CPlusPlus()); prompt_editor_->SetReadOnly(false); prompt_editor_->SetShowWhitespaces(false); - - common_tiles_editor_->SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus()); + + common_tiles_editor_->SetLanguageDefinition( + TextEditor::LanguageDefinition::CPlusPlus()); common_tiles_editor_->SetReadOnly(false); common_tiles_editor_->SetShowWhitespaces(false); - + // Ensure profiles directory exists EnsureProfilesDirectory(); } @@ -73,7 +75,8 @@ absl::Status AgentEditor::Load() { // Try to load all bot profiles auto profiles_dir = GetProfilesDirectory(); if (std::filesystem::exists(profiles_dir)) { - for (const auto& entry : std::filesystem::directory_iterator(profiles_dir)) { + for (const auto& entry : + std::filesystem::directory_iterator(profiles_dir)) { if (entry.path().extension() == ".json") { std::ifstream file(entry.path()); if (file.is_open()) { @@ -96,23 +99,24 @@ absl::Status AgentEditor::Save() { } absl::Status AgentEditor::Update() { - if (!active_) return absl::OkStatus(); - + if (!active_) + return absl::OkStatus(); + // Draw configuration dashboard DrawDashboard(); - + // Chat widget is drawn separately (not here) - + return absl::OkStatus(); } void AgentEditor::InitializeWithDependencies(ToastManager* toast_manager, - ProposalDrawer* proposal_drawer, - Rom* rom) { + ProposalDrawer* proposal_drawer, + Rom* rom) { toast_manager_ = toast_manager; proposal_drawer_ = proposal_drawer; rom_ = rom; - + if (chat_widget_) { chat_widget_->SetToastManager(toast_manager); chat_widget_->SetProposalDrawer(proposal_drawer); @@ -133,11 +137,13 @@ void AgentEditor::SetRomContext(Rom* rom) { } void AgentEditor::DrawDashboard() { - if (!active_) return; - + if (!active_) + return; + ImGui::SetNextWindowSize(ImVec2(1200, 800), ImGuiCond_FirstUseEver); - ImGui::Begin(ICON_MD_SMART_TOY " AI Agent Platform & Bot Creator", &active_, ImGuiWindowFlags_MenuBar); - + ImGui::Begin(ICON_MD_SMART_TOY " AI Agent Platform & Bot Creator", &active_, + ImGuiWindowFlags_MenuBar); + // Menu bar if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu(ICON_MD_MENU " File")) { @@ -150,386 +156,418 @@ void AgentEditor::DrawDashboard() { if (ImGui::MenuItem(ICON_MD_FILE_UPLOAD " Export Profile...")) { // TODO: Open file dialog for export if (toast_manager_) { - toast_manager_->Show("Export functionality coming soon", ToastType::kInfo); + toast_manager_->Show("Export functionality coming soon", + ToastType::kInfo); } } if (ImGui::MenuItem(ICON_MD_FILE_DOWNLOAD " Import Profile...")) { // TODO: Open file dialog for import if (toast_manager_) { - toast_manager_->Show("Import functionality coming soon", ToastType::kInfo); + toast_manager_->Show("Import functionality coming soon", + ToastType::kInfo); } } ImGui::EndMenu(); } - + if (ImGui::BeginMenu(ICON_MD_VIEW_LIST " View")) { if (ImGui::MenuItem(ICON_MD_CHAT " Open Chat Window", "Ctrl+Shift+A")) { OpenChatWindow(); } ImGui::Separator(); - ImGui::MenuItem(ICON_MD_EDIT " Show Prompt Editor", nullptr, &show_prompt_editor_); - ImGui::MenuItem(ICON_MD_FOLDER " Show Bot Profiles", nullptr, &show_bot_profiles_); - ImGui::MenuItem(ICON_MD_HISTORY " Show Chat History", nullptr, &show_chat_history_); - ImGui::MenuItem(ICON_MD_ANALYTICS " Show Metrics Dashboard", nullptr, &show_metrics_dashboard_); + ImGui::MenuItem(ICON_MD_EDIT " Show Prompt Editor", nullptr, + &show_prompt_editor_); + ImGui::MenuItem(ICON_MD_FOLDER " Show Bot Profiles", nullptr, + &show_bot_profiles_); + ImGui::MenuItem(ICON_MD_HISTORY " Show Chat History", nullptr, + &show_chat_history_); + ImGui::MenuItem(ICON_MD_ANALYTICS " Show Metrics Dashboard", nullptr, + &show_metrics_dashboard_); ImGui::EndMenu(); } - + ImGui::EndMenuBar(); } - + // Compact tabbed interface (combined tabs) if (ImGui::BeginTabBar("AgentEditorTabs", ImGuiTabBarFlags_None)) { // Bot Studio Tab - Modular 3-column layout if (ImGui::BeginTabItem(ICON_MD_SMART_TOY " Bot Studio")) { ImGui::Spacing(); - + // Three-column layout: Config+Status | Editors | Profiles - ImGuiTableFlags table_flags = ImGuiTableFlags_Resizable | - ImGuiTableFlags_BordersInnerV | + ImGuiTableFlags table_flags = ImGuiTableFlags_Resizable | + ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_SizingStretchProp; - + if (ImGui::BeginTable("BotStudioLayout", 3, table_flags)) { - ImGui::TableSetupColumn("Settings", ImGuiTableColumnFlags_WidthFixed, 320.0f); + ImGui::TableSetupColumn("Settings", ImGuiTableColumnFlags_WidthFixed, + 320.0f); ImGui::TableSetupColumn("Editors", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("Profiles", ImGuiTableColumnFlags_WidthFixed, 280.0f); + ImGui::TableSetupColumn("Profiles", ImGuiTableColumnFlags_WidthFixed, + 280.0f); ImGui::TableNextRow(); - + // Column 1: AI Provider, Behavior, ROM, Tips, Metrics (merged!) ImGui::TableNextColumn(); ImGui::PushID("SettingsColumn"); - + // Provider settings (always visible) DrawConfigurationPanel(); ImGui::Spacing(); - + // Status cards (always visible) DrawStatusPanel(); - + ImGui::PopID(); - + // Column 2: Tabbed Editors ImGui::TableNextColumn(); ImGui::PushID("EditorsColumn"); - + if (ImGui::BeginTabBar("EditorTabs", ImGuiTabBarFlags_None)) { if (ImGui::BeginTabItem(ICON_MD_EDIT " System Prompt")) { DrawPromptEditorPanel(); ImGui::EndTabItem(); } - + if (ImGui::BeginTabItem(ICON_MD_GRID_ON " Common Tiles")) { DrawCommonTilesEditor(); ImGui::EndTabItem(); } - + if (ImGui::BeginTabItem(ICON_MD_ADD " New Prompt")) { DrawNewPromptCreator(); ImGui::EndTabItem(); } - + ImGui::EndTabBar(); } - + ImGui::PopID(); - + // Column 3: Bot Profiles ImGui::TableNextColumn(); ImGui::PushID("ProfilesColumn"); DrawBotProfilesPanel(); ImGui::PopID(); - + ImGui::EndTable(); } - + ImGui::EndTabItem(); } - + // Session Manager Tab (combines History + Metrics) if (ImGui::BeginTabItem(ICON_MD_HISTORY " Sessions & History")) { ImGui::Spacing(); - + // Two-column layout - if (ImGui::BeginTable("SessionLayout", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) { - ImGui::TableSetupColumn("History", ImGuiTableColumnFlags_WidthStretch, 0.6f); - ImGui::TableSetupColumn("Metrics", ImGuiTableColumnFlags_WidthStretch, 0.4f); + if (ImGui::BeginTable( + "SessionLayout", 2, + ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) { + ImGui::TableSetupColumn("History", ImGuiTableColumnFlags_WidthStretch, + 0.6f); + ImGui::TableSetupColumn("Metrics", ImGuiTableColumnFlags_WidthStretch, + 0.4f); ImGui::TableNextRow(); - + // LEFT: Chat History ImGui::TableSetColumnIndex(0); DrawChatHistoryViewer(); - + // RIGHT: Metrics ImGui::TableSetColumnIndex(1); DrawAdvancedMetricsPanel(); - + ImGui::EndTable(); } - + ImGui::EndTabItem(); } - + ImGui::EndTabBar(); } - + ImGui::End(); } void AgentEditor::DrawConfigurationPanel() { - // Two-column layout - if (ImGui::BeginTable("ConfigLayout", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) { - ImGui::TableSetupColumn("Settings", ImGuiTableColumnFlags_WidthStretch, 0.6f); - ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthStretch, 0.4f); - ImGui::TableNextRow(); - - // LEFT: Configuration - ImGui::TableSetColumnIndex(0); - - // AI Provider Configuration - if (ImGui::CollapsingHeader(ICON_MD_SETTINGS " AI Provider", ImGuiTreeNodeFlags_DefaultOpen)) { - ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_SMART_TOY " Provider Selection"); - ImGui::Spacing(); - - // Provider buttons (large, visual) - ImVec2 button_size(ImGui::GetContentRegionAvail().x / 3 - 8, 60); - - bool is_mock = (current_profile_.provider == "mock"); - bool is_ollama = (current_profile_.provider == "ollama"); - bool is_gemini = (current_profile_.provider == "gemini"); - - if (is_mock) ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.6f, 0.6f, 0.6f, 0.8f)); - if (ImGui::Button(ICON_MD_SETTINGS " Mock", button_size)) { - current_profile_.provider = "mock"; - } - if (is_mock) ImGui::PopStyleColor(); - - ImGui::SameLine(); - if (is_ollama) ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.8f, 0.4f, 0.8f)); - if (ImGui::Button(ICON_MD_CLOUD " Ollama", button_size)) { - current_profile_.provider = "ollama"; - } - if (is_ollama) ImGui::PopStyleColor(); - - ImGui::SameLine(); - if (is_gemini) ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.196f, 0.6f, 0.8f, 0.8f)); - if (ImGui::Button(ICON_MD_SMART_TOY " Gemini", button_size)) { - current_profile_.provider = "gemini"; - } - if (is_gemini) ImGui::PopStyleColor(); - - ImGui::Separator(); - ImGui::Spacing(); - - // Provider-specific settings - if (current_profile_.provider == "ollama") { - ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.4f, 1.0f), ICON_MD_SETTINGS " Ollama Settings"); - ImGui::Text("Model:"); - ImGui::SetNextItemWidth(-1); - static char model_buf[128] = "qwen2.5-coder:7b"; - if (!current_profile_.model.empty()) { - strncpy(model_buf, current_profile_.model.c_str(), sizeof(model_buf) - 1); - } - if (ImGui::InputTextWithHint("##ollama_model", "e.g., qwen2.5-coder:7b, llama3.2", model_buf, sizeof(model_buf))) { - current_profile_.model = model_buf; - } - - ImGui::Text("Host URL:"); - ImGui::SetNextItemWidth(-1); - static char host_buf[256] = "http://localhost:11434"; - strncpy(host_buf, current_profile_.ollama_host.c_str(), sizeof(host_buf) - 1); - if (ImGui::InputText("##ollama_host", host_buf, sizeof(host_buf))) { - current_profile_.ollama_host = host_buf; - } - } else if (current_profile_.provider == "gemini") { - ImGui::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f), ICON_MD_SMART_TOY " Gemini Settings"); - - // Load from environment button - if (ImGui::Button(ICON_MD_REFRESH " Load from Environment")) { - const char* gemini_key = std::getenv("GEMINI_API_KEY"); - if (gemini_key) { - current_profile_.gemini_api_key = gemini_key; - ApplyConfig(current_config_); - if (toast_manager_) { - toast_manager_->Show("Gemini API key loaded", ToastType::kSuccess); - } - } else { - if (toast_manager_) { - toast_manager_->Show("GEMINI_API_KEY not found", ToastType::kWarning); - } - } - } - - ImGui::Spacing(); - - ImGui::Text("Model:"); - ImGui::SetNextItemWidth(-1); - static char model_buf[128] = "gemini-1.5-flash"; - if (!current_profile_.model.empty()) { - strncpy(model_buf, current_profile_.model.c_str(), sizeof(model_buf) - 1); - } - if (ImGui::InputTextWithHint("##gemini_model", "e.g., gemini-1.5-flash", model_buf, sizeof(model_buf))) { - current_profile_.model = model_buf; - } - - ImGui::Text("API Key:"); - ImGui::SetNextItemWidth(-1); - static char key_buf[256] = ""; - if (!current_profile_.gemini_api_key.empty() && key_buf[0] == '\0') { - strncpy(key_buf, current_profile_.gemini_api_key.c_str(), sizeof(key_buf) - 1); - } - if (ImGui::InputText("##gemini_key", key_buf, sizeof(key_buf), ImGuiInputTextFlags_Password)) { - current_profile_.gemini_api_key = key_buf; - } - if (!current_profile_.gemini_api_key.empty()) { - ImGui::TextColored(ImVec4(0.133f, 0.545f, 0.133f, 1.0f), ICON_MD_CHECK_CIRCLE " API key configured"); - } - } else { - ImGui::TextDisabled(ICON_MD_INFO " Mock mode - no configuration needed"); - } - } - - // Behavior Settings - if (ImGui::CollapsingHeader(ICON_MD_TUNE " Behavior", ImGuiTreeNodeFlags_DefaultOpen)) { - ImGui::Checkbox(ICON_MD_VISIBILITY " Show Reasoning", ¤t_profile_.show_reasoning); - ImGui::Checkbox(ICON_MD_ANALYTICS " Verbose Output", ¤t_profile_.verbose); - ImGui::SliderInt(ICON_MD_LOOP " Max Tool Iterations", ¤t_profile_.max_tool_iterations, 1, 10); - ImGui::SliderInt(ICON_MD_REFRESH " Max Retry Attempts", ¤t_profile_.max_retry_attempts, 1, 10); - } - - // Profile Metadata - if (ImGui::CollapsingHeader(ICON_MD_INFO " Profile Info")) { - ImGui::Text("Name:"); - static char name_buf[128]; - strncpy(name_buf, current_profile_.name.c_str(), sizeof(name_buf) - 1); - if (ImGui::InputText("##profile_name", name_buf, sizeof(name_buf))) { - current_profile_.name = name_buf; - } - - ImGui::Text("Description:"); - static char desc_buf[256]; - strncpy(desc_buf, current_profile_.description.c_str(), sizeof(desc_buf) - 1); - if (ImGui::InputTextMultiline("##profile_desc", desc_buf, sizeof(desc_buf), ImVec2(-1, 60))) { - current_profile_.description = desc_buf; - } - - ImGui::Text("Tags (comma-separated):"); - static char tags_buf[256]; - if (tags_buf[0] == '\0' && !current_profile_.tags.empty()) { - std::string tags_str; - for (size_t i = 0; i < current_profile_.tags.size(); ++i) { - if (i > 0) tags_str += ", "; - tags_str += current_profile_.tags[i]; - } - strncpy(tags_buf, tags_str.c_str(), sizeof(tags_buf) - 1); - } - if (ImGui::InputText("##profile_tags", tags_buf, sizeof(tags_buf))) { - // Parse comma-separated tags - current_profile_.tags.clear(); - std::string tags_str(tags_buf); - size_t pos = 0; - while ((pos = tags_str.find(',')) != std::string::npos) { - std::string tag = tags_str.substr(0, pos); - // Trim whitespace - tag.erase(0, tag.find_first_not_of(" \t")); - tag.erase(tag.find_last_not_of(" \t") + 1); - if (!tag.empty()) { - current_profile_.tags.push_back(tag); - } - tags_str.erase(0, pos + 1); - } - if (!tags_str.empty()) { - tags_str.erase(0, tags_str.find_first_not_of(" \t")); - tags_str.erase(tags_str.find_last_not_of(" \t") + 1); - if (!tags_str.empty()) { - current_profile_.tags.push_back(tags_str); - } - } - } - } - - // Apply button + + // AI Provider Configuration + if (ImGui::CollapsingHeader(ICON_MD_SETTINGS " AI Provider", + ImGuiTreeNodeFlags_DefaultOpen)) { + ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), + ICON_MD_SMART_TOY " Provider Selection"); ImGui::Spacing(); - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.133f, 0.545f, 0.133f, 0.8f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.133f, 0.545f, 0.133f, 1.0f)); - if (ImGui::Button(ICON_MD_CHECK " Apply & Save Configuration", ImVec2(-1, 40))) { - // Update legacy config - current_config_.provider = current_profile_.provider; - current_config_.model = current_profile_.model; - current_config_.ollama_host = current_profile_.ollama_host; - current_config_.gemini_api_key = current_profile_.gemini_api_key; - current_config_.verbose = current_profile_.verbose; - current_config_.show_reasoning = current_profile_.show_reasoning; - current_config_.max_tool_iterations = current_profile_.max_tool_iterations; - - ApplyConfig(current_config_); - Save(); - - if (toast_manager_) { - toast_manager_->Show("Configuration applied and saved", ToastType::kSuccess); + + // Provider buttons (large, visual) + ImVec2 button_size(ImGui::GetContentRegionAvail().x / 3 - 8, 60); + + bool is_mock = (current_profile_.provider == "mock"); + bool is_ollama = (current_profile_.provider == "ollama"); + bool is_gemini = (current_profile_.provider == "gemini"); + + if (is_mock) + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.6f, 0.6f, 0.6f, 0.8f)); + if (ImGui::Button(ICON_MD_SETTINGS " Mock", button_size)) { + current_profile_.provider = "mock"; + } + if (is_mock) + ImGui::PopStyleColor(); + + ImGui::SameLine(); + if (is_ollama) + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.8f, 0.4f, 0.8f)); + if (ImGui::Button(ICON_MD_CLOUD " Ollama", button_size)) { + current_profile_.provider = "ollama"; + } + if (is_ollama) + ImGui::PopStyleColor(); + + ImGui::SameLine(); + if (is_gemini) + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.196f, 0.6f, 0.8f, 0.8f)); + if (ImGui::Button(ICON_MD_SMART_TOY " Gemini", button_size)) { + current_profile_.provider = "gemini"; + } + if (is_gemini) + ImGui::PopStyleColor(); + + ImGui::Separator(); + ImGui::Spacing(); + + // Provider-specific settings + if (current_profile_.provider == "ollama") { + ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.4f, 1.0f), + ICON_MD_SETTINGS " Ollama Settings"); + ImGui::Text("Model:"); + ImGui::SetNextItemWidth(-1); + static char model_buf[128] = "qwen2.5-coder:7b"; + if (!current_profile_.model.empty()) { + strncpy(model_buf, current_profile_.model.c_str(), + sizeof(model_buf) - 1); + } + if (ImGui::InputTextWithHint("##ollama_model", + "e.g., qwen2.5-coder:7b, llama3.2", + model_buf, sizeof(model_buf))) { + current_profile_.model = model_buf; + } + + ImGui::Text("Host URL:"); + ImGui::SetNextItemWidth(-1); + static char host_buf[256] = "http://localhost:11434"; + strncpy(host_buf, current_profile_.ollama_host.c_str(), + sizeof(host_buf) - 1); + if (ImGui::InputText("##ollama_host", host_buf, sizeof(host_buf))) { + current_profile_.ollama_host = host_buf; + } + } else if (current_profile_.provider == "gemini") { + ImGui::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f), + ICON_MD_SMART_TOY " Gemini Settings"); + + // Load from environment button + if (ImGui::Button(ICON_MD_REFRESH " Load from Environment")) { + const char* gemini_key = std::getenv("GEMINI_API_KEY"); + if (gemini_key) { + current_profile_.gemini_api_key = gemini_key; + ApplyConfig(current_config_); + if (toast_manager_) { + toast_manager_->Show("Gemini API key loaded", ToastType::kSuccess); + } + } else { + if (toast_manager_) { + toast_manager_->Show("GEMINI_API_KEY not found", + ToastType::kWarning); + } + } + } + + ImGui::Spacing(); + + ImGui::Text("Model:"); + ImGui::SetNextItemWidth(-1); + static char model_buf[128] = "gemini-1.5-flash"; + if (!current_profile_.model.empty()) { + strncpy(model_buf, current_profile_.model.c_str(), + sizeof(model_buf) - 1); + } + if (ImGui::InputTextWithHint("##gemini_model", "e.g., gemini-1.5-flash", + model_buf, sizeof(model_buf))) { + current_profile_.model = model_buf; + } + + ImGui::Text("API Key:"); + ImGui::SetNextItemWidth(-1); + static char key_buf[256] = ""; + if (!current_profile_.gemini_api_key.empty() && key_buf[0] == '\0') { + strncpy(key_buf, current_profile_.gemini_api_key.c_str(), + sizeof(key_buf) - 1); + } + if (ImGui::InputText("##gemini_key", key_buf, sizeof(key_buf), + ImGuiInputTextFlags_Password)) { + current_profile_.gemini_api_key = key_buf; + } + if (!current_profile_.gemini_api_key.empty()) { + ImGui::TextColored(ImVec4(0.133f, 0.545f, 0.133f, 1.0f), + ICON_MD_CHECK_CIRCLE " API key configured"); + } + } else { + ImGui::TextDisabled(ICON_MD_INFO " Mock mode - no configuration needed"); + } + } + + // Behavior Settings + if (ImGui::CollapsingHeader(ICON_MD_TUNE " Behavior", + ImGuiTreeNodeFlags_DefaultOpen)) { + ImGui::Checkbox(ICON_MD_VISIBILITY " Show Reasoning", + ¤t_profile_.show_reasoning); + ImGui::Checkbox(ICON_MD_ANALYTICS " Verbose Output", + ¤t_profile_.verbose); + ImGui::SliderInt(ICON_MD_LOOP " Max Tool Iterations", + ¤t_profile_.max_tool_iterations, 1, 10); + ImGui::SliderInt(ICON_MD_REFRESH " Max Retry Attempts", + ¤t_profile_.max_retry_attempts, 1, 10); + } + + // Profile Metadata + if (ImGui::CollapsingHeader(ICON_MD_INFO " Profile Info")) { + ImGui::Text("Name:"); + static char name_buf[128]; + strncpy(name_buf, current_profile_.name.c_str(), sizeof(name_buf) - 1); + if (ImGui::InputText("##profile_name", name_buf, sizeof(name_buf))) { + current_profile_.name = name_buf; + } + + ImGui::Text("Description:"); + static char desc_buf[256]; + strncpy(desc_buf, current_profile_.description.c_str(), + sizeof(desc_buf) - 1); + if (ImGui::InputTextMultiline("##profile_desc", desc_buf, sizeof(desc_buf), + ImVec2(-1, 60))) { + current_profile_.description = desc_buf; + } + + ImGui::Text("Tags (comma-separated):"); + static char tags_buf[256]; + if (tags_buf[0] == '\0' && !current_profile_.tags.empty()) { + std::string tags_str; + for (size_t i = 0; i < current_profile_.tags.size(); ++i) { + if (i > 0) + tags_str += ", "; + tags_str += current_profile_.tags[i]; + } + strncpy(tags_buf, tags_str.c_str(), sizeof(tags_buf) - 1); + } + if (ImGui::InputText("##profile_tags", tags_buf, sizeof(tags_buf))) { + // Parse comma-separated tags + current_profile_.tags.clear(); + std::string tags_str(tags_buf); + size_t pos = 0; + while ((pos = tags_str.find(',')) != std::string::npos) { + std::string tag = tags_str.substr(0, pos); + // Trim whitespace + tag.erase(0, tag.find_first_not_of(" \t")); + tag.erase(tag.find_last_not_of(" \t") + 1); + if (!tag.empty()) { + current_profile_.tags.push_back(tag); + } + tags_str.erase(0, pos + 1); + } + if (!tags_str.empty()) { + tags_str.erase(0, tags_str.find_first_not_of(" \t")); + tags_str.erase(tags_str.find_last_not_of(" \t") + 1); + if (!tags_str.empty()) { + current_profile_.tags.push_back(tags_str); + } } } - ImGui::PopStyleColor(2); - - // RIGHT: Status & Quick Info - ImGui::TableSetColumnIndex(1); - - DrawStatusPanel(); - DrawMetricsPanel(); - - ImGui::EndTable(); } + + // Apply button + ImGui::Spacing(); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.133f, 0.545f, 0.133f, 0.8f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, + ImVec4(0.133f, 0.545f, 0.133f, 1.0f)); + if (ImGui::Button(ICON_MD_CHECK " Apply & Save Configuration", + ImVec2(-1, 40))) { + // Update legacy config + current_config_.provider = current_profile_.provider; + current_config_.model = current_profile_.model; + current_config_.ollama_host = current_profile_.ollama_host; + current_config_.gemini_api_key = current_profile_.gemini_api_key; + current_config_.verbose = current_profile_.verbose; + current_config_.show_reasoning = current_profile_.show_reasoning; + current_config_.max_tool_iterations = current_profile_.max_tool_iterations; + + ApplyConfig(current_config_); + Save(); + + if (toast_manager_) { + toast_manager_->Show("Configuration applied and saved", + ToastType::kSuccess); + } + } + ImGui::PopStyleColor(2); + + DrawMetricsPanel(); } void AgentEditor::DrawStatusPanel() { // Always visible status cards (no collapsing) - + // Chat Status Card ImGui::BeginChild("ChatStatusCard", ImVec2(0, 100), true); ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_CHAT " Chat"); ImGui::Separator(); - + if (chat_widget_ && chat_widget_->is_active()) { - ImGui::TextColored(ImVec4(0.133f, 0.545f, 0.133f, 1.0f), ICON_MD_CHECK_CIRCLE " Active"); + ImGui::TextColored(ImVec4(0.133f, 0.545f, 0.133f, 1.0f), + ICON_MD_CHECK_CIRCLE " Active"); } else { ImGui::TextDisabled(ICON_MD_CANCEL " Inactive"); } - + ImGui::Spacing(); if (ImGui::Button(ICON_MD_OPEN_IN_NEW " Open", ImVec2(-1, 0))) { OpenChatWindow(); } ImGui::EndChild(); - + ImGui::Spacing(); - + // ROM Context Card ImGui::BeginChild("RomStatusCard", ImVec2(0, 100), true); ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_GAMEPAD " ROM"); ImGui::Separator(); - + if (rom_ && rom_->is_loaded()) { - ImGui::TextColored(ImVec4(0.133f, 0.545f, 0.133f, 1.0f), ICON_MD_CHECK_CIRCLE " Loaded"); + ImGui::TextColored(ImVec4(0.133f, 0.545f, 0.133f, 1.0f), + ICON_MD_CHECK_CIRCLE " Loaded"); ImGui::TextDisabled("Title: %s", rom_->title().c_str()); ImGui::TextDisabled("Tools: Ready"); } else { - ImGui::TextColored(ImVec4(0.8f, 0.2f, 0.2f, 1.0f), ICON_MD_WARNING " Not Loaded"); + ImGui::TextColored(ImVec4(0.8f, 0.2f, 0.2f, 1.0f), + ICON_MD_WARNING " Not Loaded"); ImGui::TextDisabled("Load ROM for AI tools"); } ImGui::EndChild(); - + ImGui::Spacing(); - + // Quick Tips Card ImGui::BeginChild("QuickTipsCard", ImVec2(0, 150), true); - ImGui::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f), ICON_MD_TIPS_AND_UPDATES " Quick Tips"); + ImGui::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f), + ICON_MD_TIPS_AND_UPDATES " Quick Tips"); ImGui::Separator(); ImGui::Spacing(); - + ImGui::BulletText("Ctrl+H: Toggle chat popup"); ImGui::BulletText("Ctrl+P: View proposals"); ImGui::BulletText("Edit prompts in center"); ImGui::BulletText("Create custom bots"); ImGui::BulletText("Save/load chat sessions"); - + ImGui::EndChild(); } @@ -545,29 +583,33 @@ void AgentEditor::DrawMetricsPanel() { } void AgentEditor::DrawPromptEditorPanel() { - ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_EDIT " Prompt Editor"); + ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), + ICON_MD_EDIT " Prompt Editor"); ImGui::Separator(); ImGui::Spacing(); - + // Compact prompt file selector ImGui::Text("File:"); ImGui::SetNextItemWidth(-45); if (ImGui::BeginCombo("##prompt_file", active_prompt_file_.c_str())) { - if (ImGui::Selectable("system_prompt.txt", active_prompt_file_ == "system_prompt.txt")) { + if (ImGui::Selectable("system_prompt.txt", + active_prompt_file_ == "system_prompt.txt")) { active_prompt_file_ = "system_prompt.txt"; prompt_editor_initialized_ = false; } - if (ImGui::Selectable("system_prompt_v2.txt", active_prompt_file_ == "system_prompt_v2.txt")) { + if (ImGui::Selectable("system_prompt_v2.txt", + active_prompt_file_ == "system_prompt_v2.txt")) { active_prompt_file_ = "system_prompt_v2.txt"; prompt_editor_initialized_ = false; } - if (ImGui::Selectable("system_prompt_v3.txt", active_prompt_file_ == "system_prompt_v3.txt")) { + if (ImGui::Selectable("system_prompt_v3.txt", + active_prompt_file_ == "system_prompt_v3.txt")) { active_prompt_file_ = "system_prompt_v3.txt"; prompt_editor_initialized_ = false; } ImGui::EndCombo(); } - + ImGui::SameLine(); if (ImGui::SmallButton(ICON_MD_REFRESH)) { prompt_editor_initialized_ = false; @@ -575,27 +617,27 @@ void AgentEditor::DrawPromptEditorPanel() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Reload from disk"); } - + // Load prompt file if not initialized if (!prompt_editor_initialized_ && prompt_editor_) { std::string asset_path = "agent/" + active_prompt_file_; auto content_result = core::AssetLoader::LoadTextFile(asset_path); - + if (content_result.ok()) { prompt_editor_->SetText(*content_result); current_profile_.system_prompt = *content_result; prompt_editor_initialized_ = true; - + if (toast_manager_) { - toast_manager_->Show( - absl::StrFormat(ICON_MD_CHECK_CIRCLE " Loaded %s", active_prompt_file_), - ToastType::kSuccess, 2.0f); + toast_manager_->Show(absl::StrFormat(ICON_MD_CHECK_CIRCLE " Loaded %s", + active_prompt_file_), + ToastType::kSuccess, 2.0f); } } else { // Show detailed error in console std::cerr << "❌ Failed to load " << active_prompt_file_ << "\n"; std::cerr << " Error: " << content_result.status().message() << "\n"; - + // Set placeholder with instructions std::string placeholder = absl::StrFormat( "# System prompt file not found: %s\n" @@ -603,59 +645,65 @@ void AgentEditor::DrawPromptEditorPanel() { "# Please ensure the file exists in:\n" "# - assets/agent/%s\n" "# - Or Contents/Resources/agent/%s (macOS bundle)\n\n" - "# You can create a custom prompt here and save it to your bot profile.", - active_prompt_file_, - content_result.status().message(), - active_prompt_file_, - active_prompt_file_); - + "# You can create a custom prompt here and save it to your bot " + "profile.", + active_prompt_file_, content_result.status().message(), + active_prompt_file_, active_prompt_file_); + prompt_editor_->SetText(placeholder); prompt_editor_initialized_ = true; } } - + ImGui::Spacing(); - + // Text editor if (prompt_editor_) { - ImVec2 editor_size = ImVec2(ImGui::GetContentRegionAvail().x, + ImVec2 editor_size = ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y - 50); prompt_editor_->Render("##prompt_editor", editor_size, true); - + // Save button ImGui::Spacing(); if (ImGui::Button(ICON_MD_SAVE " Save Prompt to Profile", ImVec2(-1, 0))) { current_profile_.system_prompt = prompt_editor_->GetText(); if (toast_manager_) { - toast_manager_->Show("System prompt saved to profile", ToastType::kSuccess); + toast_manager_->Show("System prompt saved to profile", + ToastType::kSuccess); } } } - + ImGui::Spacing(); - ImGui::TextWrapped("Edit the system prompt that guides the AI agent's behavior. Changes are saved to the current bot profile."); + ImGui::TextWrapped( + "Edit the system prompt that guides the AI agent's behavior. Changes are " + "saved to the current bot profile."); } void AgentEditor::DrawBotProfilesPanel() { - ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_FOLDER " Bot Profile Manager"); + ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), + ICON_MD_FOLDER " Bot Profile Manager"); ImGui::Separator(); ImGui::Spacing(); - + // Current profile display ImGui::BeginChild("CurrentProfile", ImVec2(0, 150), true); - ImGui::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f), ICON_MD_STAR " Current Profile"); + ImGui::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f), + ICON_MD_STAR " Current Profile"); ImGui::Separator(); ImGui::Text("Name: %s", current_profile_.name.c_str()); ImGui::Text("Provider: %s", current_profile_.provider.c_str()); if (!current_profile_.model.empty()) { ImGui::Text("Model: %s", current_profile_.model.c_str()); } - ImGui::TextWrapped("Description: %s", - current_profile_.description.empty() ? "No description" : current_profile_.description.c_str()); + ImGui::TextWrapped("Description: %s", + current_profile_.description.empty() + ? "No description" + : current_profile_.description.c_str()); ImGui::EndChild(); - + ImGui::Spacing(); - + // Profile management buttons if (ImGui::Button(ICON_MD_ADD " Create New Profile", ImVec2(-1, 0))) { // Create new profile from current @@ -665,71 +713,83 @@ void AgentEditor::DrawBotProfilesPanel() { new_profile.modified_at = absl::Now(); current_profile_ = new_profile; if (toast_manager_) { - toast_manager_->Show("New profile created. Configure and save it.", ToastType::kInfo); + toast_manager_->Show("New profile created. Configure and save it.", + ToastType::kInfo); } } - + ImGui::Spacing(); - + // Saved profiles list - ImGui::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f), ICON_MD_LIST " Saved Profiles"); + ImGui::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f), + ICON_MD_LIST " Saved Profiles"); ImGui::Separator(); - + ImGui::BeginChild("ProfilesList", ImVec2(0, 0), true); - + if (loaded_profiles_.empty()) { - ImGui::TextDisabled("No saved profiles. Create and save a profile to see it here."); + ImGui::TextDisabled( + "No saved profiles. Create and save a profile to see it here."); } else { for (size_t i = 0; i < loaded_profiles_.size(); ++i) { const auto& profile = loaded_profiles_[i]; ImGui::PushID(static_cast(i)); - + bool is_current = (profile.name == current_profile_.name); if (is_current) { - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.196f, 0.6f, 0.8f, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_Button, + ImVec4(0.196f, 0.6f, 0.8f, 0.6f)); } - - if (ImGui::Button(profile.name.c_str(), ImVec2(ImGui::GetContentRegionAvail().x - 80, 0))) { + + if (ImGui::Button(profile.name.c_str(), + ImVec2(ImGui::GetContentRegionAvail().x - 80, 0))) { LoadBotProfile(profile.name); if (toast_manager_) { - toast_manager_->Show(absl::StrFormat("Loaded profile: %s", profile.name), ToastType::kSuccess); + toast_manager_->Show( + absl::StrFormat("Loaded profile: %s", profile.name), + ToastType::kSuccess); } } - + if (is_current) { ImGui::PopStyleColor(); } - + ImGui::SameLine(); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.2f, 0.2f, 0.6f)); if (ImGui::SmallButton(ICON_MD_DELETE)) { DeleteBotProfile(profile.name); if (toast_manager_) { - toast_manager_->Show(absl::StrFormat("Deleted profile: %s", profile.name), ToastType::kInfo); + toast_manager_->Show( + absl::StrFormat("Deleted profile: %s", profile.name), + ToastType::kInfo); } } ImGui::PopStyleColor(); - - ImGui::TextDisabled(" %s | %s", profile.provider.c_str(), - profile.description.empty() ? "No description" : profile.description.c_str()); + + ImGui::TextDisabled(" %s | %s", profile.provider.c_str(), + profile.description.empty() + ? "No description" + : profile.description.c_str()); ImGui::Spacing(); - + ImGui::PopID(); } } - + ImGui::EndChild(); } void AgentEditor::DrawChatHistoryViewer() { - ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_HISTORY " Chat History Viewer"); + ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), + ICON_MD_HISTORY " Chat History Viewer"); ImGui::Separator(); ImGui::Spacing(); - + if (ImGui::Button(ICON_MD_REFRESH " Refresh History")) { history_needs_refresh_ = true; } - + ImGui::SameLine(); if (ImGui::Button(ICON_MD_DELETE " Clear History")) { if (chat_widget_) { @@ -739,99 +799,111 @@ void AgentEditor::DrawChatHistoryViewer() { } } } - + ImGui::Spacing(); ImGui::Separator(); - + // Get history from chat widget if (chat_widget_ && history_needs_refresh_) { // Access the service's history through the chat widget // For now, show a placeholder history_needs_refresh_ = false; } - + ImGui::BeginChild("HistoryList", ImVec2(0, 0), true); - + if (cached_history_.empty()) { - ImGui::TextDisabled("No chat history. Start a conversation in the chat window."); + ImGui::TextDisabled( + "No chat history. Start a conversation in the chat window."); } else { for (const auto& msg : cached_history_) { bool from_user = (msg.sender == cli::agent::ChatMessage::Sender::kUser); - ImVec4 color = from_user ? ImVec4(0.6f, 0.8f, 1.0f, 1.0f) : ImVec4(0.4f, 0.8f, 0.4f, 1.0f); - + ImVec4 color = from_user ? ImVec4(0.6f, 0.8f, 1.0f, 1.0f) + : ImVec4(0.4f, 0.8f, 0.4f, 1.0f); + ImGui::PushStyleColor(ImGuiCol_Text, color); ImGui::Text("%s:", from_user ? "User" : "Agent"); ImGui::PopStyleColor(); - + ImGui::SameLine(); - ImGui::TextDisabled("%s", absl::FormatTime("%H:%M:%S", msg.timestamp, absl::LocalTimeZone()).c_str()); - + ImGui::TextDisabled("%s", absl::FormatTime("%H:%M:%S", msg.timestamp, + absl::LocalTimeZone()) + .c_str()); + ImGui::TextWrapped("%s", msg.message.c_str()); ImGui::Spacing(); ImGui::Separator(); } } - + ImGui::EndChild(); } void AgentEditor::DrawAdvancedMetricsPanel() { - ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_ANALYTICS " Session Metrics & Analytics"); + ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), + ICON_MD_ANALYTICS " Session Metrics & Analytics"); ImGui::Separator(); ImGui::Spacing(); - + // Get metrics from chat widget service if (chat_widget_) { // For now show placeholder metrics structure - if (ImGui::BeginTable("MetricsTable", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { - ImGui::TableSetupColumn("Metric", ImGuiTableColumnFlags_WidthFixed, 200.0f); + if (ImGui::BeginTable("MetricsTable", 2, + ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + ImGui::TableSetupColumn("Metric", ImGuiTableColumnFlags_WidthFixed, + 200.0f); ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch); ImGui::TableHeadersRow(); - + ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::Text(ICON_MD_CHAT " Total Messages"); ImGui::TableSetColumnIndex(1); ImGui::TextDisabled("Available in chat session"); - + ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::Text(ICON_MD_BUILD " Tool Calls"); ImGui::TableSetColumnIndex(1); ImGui::TextDisabled("Available in chat session"); - + ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::Text(ICON_MD_PREVIEW " Proposals Created"); ImGui::TableSetColumnIndex(1); ImGui::TextDisabled("Available in chat session"); - + ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::Text(ICON_MD_TIMER " Average Latency"); ImGui::TableSetColumnIndex(1); ImGui::TextDisabled("Available in chat session"); - + ImGui::EndTable(); } - + ImGui::Spacing(); - ImGui::TextWrapped("Detailed session metrics are available during active chat sessions. Open the chat window to see live statistics."); + ImGui::TextWrapped( + "Detailed session metrics are available during active chat sessions. " + "Open the chat window to see live statistics."); } else { - ImGui::TextDisabled("No metrics available. Initialize the chat system first."); + ImGui::TextDisabled( + "No metrics available. Initialize the chat system first."); } } void AgentEditor::DrawCommonTilesEditor() { - ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_GRID_ON " Common Tiles Reference"); + ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), + ICON_MD_GRID_ON " Common Tiles Reference"); ImGui::Separator(); ImGui::Spacing(); - - ImGui::TextWrapped("Customize the tile reference file that AI uses for tile placement. " - "Organize tiles by category and provide hex IDs with descriptions."); - + + ImGui::TextWrapped( + "Customize the tile reference file that AI uses for tile placement. " + "Organize tiles by category and provide hex IDs with descriptions."); + ImGui::Spacing(); - + // Load/Save buttons if (ImGui::Button(ICON_MD_FOLDER_OPEN " Load", ImVec2(100, 0))) { auto content = core::AssetLoader::LoadTextFile("agent/common_tiles.txt"); @@ -839,19 +911,22 @@ void AgentEditor::DrawCommonTilesEditor() { common_tiles_editor_->SetText(*content); common_tiles_initialized_ = true; if (toast_manager_) { - toast_manager_->Show(ICON_MD_CHECK_CIRCLE " Common tiles loaded", ToastType::kSuccess, 2.0f); + toast_manager_->Show(ICON_MD_CHECK_CIRCLE " Common tiles loaded", + ToastType::kSuccess, 2.0f); } } } - + ImGui::SameLine(); if (ImGui::Button(ICON_MD_SAVE " Save", ImVec2(100, 0))) { // Save to project or assets directory if (toast_manager_) { - toast_manager_->Show(ICON_MD_INFO " Save to project directory (coming soon)", ToastType::kInfo, 2.0f); + toast_manager_->Show(ICON_MD_INFO + " Save to project directory (coming soon)", + ToastType::kInfo, 2.0f); } } - + ImGui::SameLine(); if (ImGui::SmallButton(ICON_MD_REFRESH)) { common_tiles_initialized_ = false; @@ -859,7 +934,7 @@ void AgentEditor::DrawCommonTilesEditor() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Reload from disk"); } - + // Load if not initialized if (!common_tiles_initialized_ && common_tiles_editor_) { auto content = core::AssetLoader::LoadTextFile("agent/common_tiles.txt"); @@ -867,7 +942,7 @@ void AgentEditor::DrawCommonTilesEditor() { common_tiles_editor_->SetText(*content); } else { // Create default template - std::string default_tiles = + std::string default_tiles = "# Common Tile16 Reference\n" "# Format: 0xHEX = Description\n\n" "[grass_tiles]\n" @@ -882,38 +957,41 @@ void AgentEditor::DrawCommonTilesEditor() { } common_tiles_initialized_ = true; } - + ImGui::Separator(); ImGui::Spacing(); - + // Editor if (common_tiles_editor_) { - ImVec2 editor_size(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y); + ImVec2 editor_size(ImGui::GetContentRegionAvail().x, + ImGui::GetContentRegionAvail().y); common_tiles_editor_->Render("##tiles_editor", editor_size, true); } } void AgentEditor::DrawNewPromptCreator() { - ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_ADD " Create New System Prompt"); + ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), + ICON_MD_ADD " Create New System Prompt"); ImGui::Separator(); ImGui::Spacing(); - - ImGui::TextWrapped("Create a custom system prompt from scratch or use a template."); - + + ImGui::TextWrapped( + "Create a custom system prompt from scratch or use a template."); + ImGui::Spacing(); ImGui::Separator(); - + // Prompt name input ImGui::Text("Prompt Name:"); ImGui::SetNextItemWidth(-1); - ImGui::InputTextWithHint("##new_prompt_name", "e.g., custom_prompt.txt", + ImGui::InputTextWithHint("##new_prompt_name", "e.g., custom_prompt.txt", new_prompt_name_, sizeof(new_prompt_name_)); - + ImGui::Spacing(); - + // Template selection ImGui::Text("Start from template:"); - + if (ImGui::Button(ICON_MD_FILE_COPY " v1 (Basic)", ImVec2(-1, 0))) { auto content = core::AssetLoader::LoadTextFile("agent/system_prompt.txt"); if (content.ok() && prompt_editor_) { @@ -923,9 +1001,10 @@ void AgentEditor::DrawNewPromptCreator() { } } } - + if (ImGui::Button(ICON_MD_FILE_COPY " v2 (Enhanced)", ImVec2(-1, 0))) { - auto content = core::AssetLoader::LoadTextFile("agent/system_prompt_v2.txt"); + auto content = + core::AssetLoader::LoadTextFile("agent/system_prompt_v2.txt"); if (content.ok() && prompt_editor_) { prompt_editor_->SetText(*content); if (toast_manager_) { @@ -933,9 +1012,10 @@ void AgentEditor::DrawNewPromptCreator() { } } } - + if (ImGui::Button(ICON_MD_FILE_COPY " v3 (Proactive)", ImVec2(-1, 0))) { - auto content = core::AssetLoader::LoadTextFile("agent/system_prompt_v3.txt"); + auto content = + core::AssetLoader::LoadTextFile("agent/system_prompt_v3.txt"); if (content.ok() && prompt_editor_) { prompt_editor_->SetText(*content); if (toast_manager_) { @@ -943,10 +1023,10 @@ void AgentEditor::DrawNewPromptCreator() { } } } - + if (ImGui::Button(ICON_MD_NOTE_ADD " Blank Template", ImVec2(-1, 0))) { if (prompt_editor_) { - std::string blank_template = + std::string blank_template = "# Custom System Prompt\n\n" "You are an AI assistant for ROM hacking.\n\n" "## Your Role\n" @@ -964,14 +1044,15 @@ void AgentEditor::DrawNewPromptCreator() { "3. Explain your reasoning\n"; prompt_editor_->SetText(blank_template); if (toast_manager_) { - toast_manager_->Show("Blank template created", ToastType::kSuccess, 1.5f); + toast_manager_->Show("Blank template created", ToastType::kSuccess, + 1.5f); } } } - + ImGui::Spacing(); ImGui::Separator(); - + // Save button ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.133f, 0.545f, 0.133f, 0.8f)); if (ImGui::Button(ICON_MD_SAVE " Save New Prompt", ImVec2(-1, 40))) { @@ -981,67 +1062,73 @@ void AgentEditor::DrawNewPromptCreator() { if (!absl::EndsWith(filename, ".txt")) { filename += ".txt"; } - + // TODO: Actually save the file if (toast_manager_) { toast_manager_->Show( absl::StrFormat(ICON_MD_SAVE " Prompt saved as %s", filename), ToastType::kSuccess, 3.0f); } - + // Clear name buffer std::memset(new_prompt_name_, 0, sizeof(new_prompt_name_)); } else if (toast_manager_) { - toast_manager_->Show(ICON_MD_WARNING " Enter a name for the prompt", ToastType::kWarning, 2.0f); + toast_manager_->Show(ICON_MD_WARNING " Enter a name for the prompt", + ToastType::kWarning, 2.0f); } } ImGui::PopStyleColor(); - + ImGui::Spacing(); - ImGui::TextWrapped("Note: New prompts are saved to your project. Use 'System Prompt' tab to edit existing prompts."); + ImGui::TextWrapped( + "Note: New prompts are saved to your project. Use 'System Prompt' tab to " + "edit existing prompts."); } // Bot Profile Management Implementation absl::Status AgentEditor::SaveBotProfile(const BotProfile& profile) { #if defined(YAZE_WITH_JSON) RETURN_IF_ERROR(EnsureProfilesDirectory()); - - std::filesystem::path profile_path = GetProfilesDirectory() / (profile.name + ".json"); + + std::filesystem::path profile_path = + GetProfilesDirectory() / (profile.name + ".json"); std::ofstream file(profile_path); if (!file.is_open()) { return absl::InternalError("Failed to open profile file for writing"); } - + file << ProfileToJson(profile); file.close(); - + // Reload profiles list Load(); - + return absl::OkStatus(); #else - return absl::UnimplementedError("JSON support required for profile management"); + return absl::UnimplementedError( + "JSON support required for profile management"); #endif } absl::Status AgentEditor::LoadBotProfile(const std::string& name) { #if defined(YAZE_WITH_JSON) - std::filesystem::path profile_path = GetProfilesDirectory() / (name + ".json"); + std::filesystem::path profile_path = + GetProfilesDirectory() / (name + ".json"); if (!std::filesystem::exists(profile_path)) { return absl::NotFoundError(absl::StrFormat("Profile '%s' not found", name)); } - + std::ifstream file(profile_path); if (!file.is_open()) { return absl::InternalError("Failed to open profile file"); } - + std::string json_content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - + ASSIGN_OR_RETURN(auto profile, JsonToProfile(json_content)); current_profile_ = profile; - + // Update legacy config current_config_.provider = profile.provider; current_config_.model = profile.model; @@ -1050,27 +1137,29 @@ absl::Status AgentEditor::LoadBotProfile(const std::string& name) { current_config_.verbose = profile.verbose; current_config_.show_reasoning = profile.show_reasoning; current_config_.max_tool_iterations = profile.max_tool_iterations; - + // Apply to chat widget ApplyConfig(current_config_); - + return absl::OkStatus(); #else - return absl::UnimplementedError("JSON support required for profile management"); + return absl::UnimplementedError( + "JSON support required for profile management"); #endif } absl::Status AgentEditor::DeleteBotProfile(const std::string& name) { - std::filesystem::path profile_path = GetProfilesDirectory() / (name + ".json"); + std::filesystem::path profile_path = + GetProfilesDirectory() / (name + ".json"); if (!std::filesystem::exists(profile_path)) { return absl::NotFoundError(absl::StrFormat("Profile '%s' not found", name)); } - + std::filesystem::remove(profile_path); - + // Reload profiles list Load(); - + return absl::OkStatus(); } @@ -1080,7 +1169,7 @@ std::vector AgentEditor::GetAllProfiles() const { void AgentEditor::SetCurrentProfile(const BotProfile& profile) { current_profile_ = profile; - + // Update legacy config current_config_.provider = profile.provider; current_config_.model = profile.model; @@ -1091,16 +1180,17 @@ void AgentEditor::SetCurrentProfile(const BotProfile& profile) { current_config_.max_tool_iterations = profile.max_tool_iterations; } -absl::Status AgentEditor::ExportProfile(const BotProfile& profile, const std::filesystem::path& path) { +absl::Status AgentEditor::ExportProfile(const BotProfile& profile, + const std::filesystem::path& path) { #if defined(YAZE_WITH_JSON) std::ofstream file(path); if (!file.is_open()) { return absl::InternalError("Failed to open export file"); } - + file << ProfileToJson(profile); file.close(); - + return absl::OkStatus(); #else return absl::UnimplementedError("JSON support required"); @@ -1112,17 +1202,17 @@ absl::Status AgentEditor::ImportProfile(const std::filesystem::path& path) { if (!std::filesystem::exists(path)) { return absl::NotFoundError("Import file not found"); } - + std::ifstream file(path); if (!file.is_open()) { return absl::InternalError("Failed to open import file"); } - + std::string json_content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - + ASSIGN_OR_RETURN(auto profile, JsonToProfile(json_content)); - + // Save as new profile return SaveBotProfile(profile); #else @@ -1145,8 +1235,9 @@ std::filesystem::path AgentEditor::GetProfilesDirectory() const { } #endif } - - return config_dir / std::filesystem::path("agent") / std::filesystem::path("profiles"); + + return config_dir / std::filesystem::path("agent") / + std::filesystem::path("profiles"); } absl::Status AgentEditor::EnsureProfilesDirectory() { @@ -1154,7 +1245,8 @@ absl::Status AgentEditor::EnsureProfilesDirectory() { std::error_code ec; std::filesystem::create_directories(dir, ec); if (ec) { - return absl::InternalError(absl::StrFormat("Failed to create profiles directory: %s", ec.message())); + return absl::InternalError(absl::StrFormat( + "Failed to create profiles directory: %s", ec.message())); } return absl::OkStatus(); } @@ -1174,20 +1266,23 @@ std::string AgentEditor::ProfileToJson(const BotProfile& profile) const { json["max_tool_iterations"] = profile.max_tool_iterations; json["max_retry_attempts"] = profile.max_retry_attempts; json["tags"] = profile.tags; - json["created_at"] = absl::FormatTime(absl::RFC3339_full, profile.created_at, absl::UTCTimeZone()); - json["modified_at"] = absl::FormatTime(absl::RFC3339_full, profile.modified_at, absl::UTCTimeZone()); - + json["created_at"] = absl::FormatTime(absl::RFC3339_full, profile.created_at, + absl::UTCTimeZone()); + json["modified_at"] = absl::FormatTime( + absl::RFC3339_full, profile.modified_at, absl::UTCTimeZone()); + return json.dump(2); #else return "{}"; #endif } -absl::StatusOr AgentEditor::JsonToProfile(const std::string& json_str) const { +absl::StatusOr AgentEditor::JsonToProfile( + const std::string& json_str) const { #if defined(YAZE_WITH_JSON) try { nlohmann::json json = nlohmann::json::parse(json_str); - + BotProfile profile; profile.name = json.value("name", "Unnamed Profile"); profile.description = json.value("description", ""); @@ -1200,30 +1295,35 @@ absl::StatusOr AgentEditor::JsonToProfile(const std::st profile.show_reasoning = json.value("show_reasoning", true); profile.max_tool_iterations = json.value("max_tool_iterations", 4); profile.max_retry_attempts = json.value("max_retry_attempts", 3); - + if (json.contains("tags") && json["tags"].is_array()) { for (const auto& tag : json["tags"]) { profile.tags.push_back(tag.get()); } } - + if (json.contains("created_at")) { absl::Time created; - if (absl::ParseTime(absl::RFC3339_full, json["created_at"].get(), &created, nullptr)) { + if (absl::ParseTime(absl::RFC3339_full, + json["created_at"].get(), &created, + nullptr)) { profile.created_at = created; } } - + if (json.contains("modified_at")) { absl::Time modified; - if (absl::ParseTime(absl::RFC3339_full, json["modified_at"].get(), &modified, nullptr)) { + if (absl::ParseTime(absl::RFC3339_full, + json["modified_at"].get(), &modified, + nullptr)) { profile.modified_at = modified; } } - + return profile; } catch (const std::exception& e) { - return absl::InternalError(absl::StrFormat("Failed to parse profile JSON: %s", e.what())); + return absl::InternalError( + absl::StrFormat("Failed to parse profile JSON: %s", e.what())); } #else return absl::UnimplementedError("JSON support required"); @@ -1237,7 +1337,7 @@ AgentEditor::AgentConfig AgentEditor::GetCurrentConfig() const { void AgentEditor::ApplyConfig(const AgentConfig& config) { current_config_ = config; - + // Apply to chat widget if available if (chat_widget_) { AgentChatWidget::AgentConfigState chat_config; @@ -1277,8 +1377,9 @@ absl::StatusOr AgentEditor::HostSession( current_mode_ = mode; if (mode == CollaborationMode::kLocal) { - ASSIGN_OR_RETURN(auto session, local_coordinator_->HostSession(session_name)); - + ASSIGN_OR_RETURN(auto session, + local_coordinator_->HostSession(session_name)); + SessionInfo info; info.session_id = session.session_id; info.session_name = session.session_name; @@ -1320,7 +1421,7 @@ absl::StatusOr AgentEditor::HostSession( ASSIGN_OR_RETURN(auto session, network_coordinator_->HostSession(session_name, username)); - + SessionInfo info; info.session_id = session.session_id; info.session_name = session.session_name; @@ -1349,8 +1450,9 @@ absl::StatusOr AgentEditor::JoinSession( current_mode_ = mode; if (mode == CollaborationMode::kLocal) { - ASSIGN_OR_RETURN(auto session, local_coordinator_->JoinSession(session_code)); - + ASSIGN_OR_RETURN(auto session, + local_coordinator_->JoinSession(session_code)); + SessionInfo info; info.session_id = session.session_id; info.session_name = session.session_name; @@ -1392,7 +1494,7 @@ absl::StatusOr AgentEditor::JoinSession( ASSIGN_OR_RETURN(auto session, network_coordinator_->JoinSession(session_code, username)); - + SessionInfo info; info.session_id = session.session_id; info.session_name = session.session_name; @@ -1456,7 +1558,7 @@ absl::StatusOr AgentEditor::RefreshSession() { if (current_mode_ == CollaborationMode::kLocal) { ASSIGN_OR_RETURN(auto session, local_coordinator_->RefreshSession()); - + SessionInfo info; info.session_id = session.session_id; info.session_name = session.session_name; @@ -1494,13 +1596,13 @@ absl::Status AgentEditor::ConnectToServer(const std::string& server_url) { try { network_coordinator_ = std::make_unique(server_url); - + if (toast_manager_) { toast_manager_->Show( absl::StrFormat("Connected to server: %s", server_url), ToastType::kSuccess, 3.0f); } - + return absl::OkStatus(); } catch (const std::exception& e) { return absl::InternalError( @@ -1513,7 +1615,7 @@ void AgentEditor::DisconnectFromServer() { LeaveSession(); } network_coordinator_.reset(); - + if (toast_manager_) { toast_manager_->Show("Disconnected from server", ToastType::kInfo, 2.5f); } @@ -1550,12 +1652,13 @@ void AgentEditor::SetupChatWidgetCallbacks() { } AgentChatWidget::CollaborationCallbacks collab_callbacks; - - collab_callbacks.host_session = - [this](const std::string& session_name) - -> absl::StatusOr { - ASSIGN_OR_RETURN(auto session, this->HostSession(session_name, current_mode_)); - + + collab_callbacks.host_session = [this](const std::string& session_name) + -> absl::StatusOr< + AgentChatWidget::CollaborationCallbacks::SessionContext> { + ASSIGN_OR_RETURN(auto session, + this->HostSession(session_name, current_mode_)); + AgentChatWidget::CollaborationCallbacks::SessionContext context; context.session_id = session.session_id; context.session_name = session.session_name; @@ -1563,11 +1666,12 @@ void AgentEditor::SetupChatWidgetCallbacks() { return context; }; - collab_callbacks.join_session = - [this](const std::string& session_code) - -> absl::StatusOr { - ASSIGN_OR_RETURN(auto session, this->JoinSession(session_code, current_mode_)); - + collab_callbacks.join_session = [this](const std::string& session_code) + -> absl::StatusOr< + AgentChatWidget::CollaborationCallbacks::SessionContext> { + ASSIGN_OR_RETURN(auto session, + this->JoinSession(session_code, current_mode_)); + AgentChatWidget::CollaborationCallbacks::SessionContext context; context.session_id = session.session_id; context.session_name = session.session_name; @@ -1580,9 +1684,10 @@ void AgentEditor::SetupChatWidgetCallbacks() { }; collab_callbacks.refresh_session = - [this]() -> absl::StatusOr { + [this]() -> absl::StatusOr< + AgentChatWidget::CollaborationCallbacks::SessionContext> { ASSIGN_OR_RETURN(auto session, this->RefreshSession()); - + AgentChatWidget::CollaborationCallbacks::SessionContext context; context.session_id = session.session_id; context.session_name = session.session_name; diff --git a/src/app_main.cc b/src/app_main.cc deleted file mode 100644 index 0c8c1c1e..00000000 --- a/src/app_main.cc +++ /dev/null @@ -1,89 +0,0 @@ -// Application entry point - separated from C API implementation -#include "yaze.h" - -#include -#include -#include -#include -#include -#include - -#include "app/core/controller.h" -#include "app/core/platform/app_delegate.h" -#include "util/flag.h" -#include "util/log.h" -#include "yaze_config.h" - -DEFINE_FLAG(std::string, rom_file, "", - "Path to the ROM file to load. " - "If not specified, the app will run without a ROM."); - -DEFINE_FLAG( - std::string, log_level, "info", - "Minimum log level to output (e.g., debug, info, warn, error, fatal)."); -DEFINE_FLAG(std::string, log_file, "", - "Path to the log file. If empty, logs to stderr."); -DEFINE_FLAG(std::string, log_categories, "", - "Comma-separated list of log categories to enable."); - -int yaze_app_main(int argc, char** argv) { - yaze::util::FlagParser parser(yaze::util::global_flag_registry()); - RETURN_IF_EXCEPTION(parser.Parse(argc, argv)); - - // --- Configure Logging System --- - auto string_to_log_level = [](const std::string& s) { - std::string upper_s; - std::transform(s.begin(), s.end(), std::back_inserter(upper_s), - ::toupper); - if (upper_s == "YAZE_DEBUG") return yaze::util::LogLevel::YAZE_DEBUG; - if (upper_s == "INFO") return yaze::util::LogLevel::INFO; - if (upper_s == "WARN" || upper_s == "WARNING") - return yaze::util::LogLevel::WARNING; - if (upper_s == "ERROR") return yaze::util::LogLevel::ERROR; - if (upper_s == "FATAL") return yaze::util::LogLevel::FATAL; - return yaze::util::LogLevel::INFO; // Default - }; - - auto split_categories = [](const std::string& s) { - std::set result; - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, ',')) { - if (!item.empty()) { - result.insert(item); - } - } - return result; - }; - - yaze::util::LogManager::instance().configure( - string_to_log_level(FLAGS_log_level->Get()), FLAGS_log_file->Get(), - split_categories(FLAGS_log_categories->Get())); - - LOG_INFO("App", "Yaze starting up..."); - LOG_INFO("App", "Version: %s", YAZE_VERSION_STRING); - - std::string rom_filename = ""; - if (!FLAGS_rom_file->Get().empty()) { - rom_filename = FLAGS_rom_file->Get(); - LOG_INFO("App", "Loading ROM file: %s", rom_filename); - } - -#ifdef __APPLE__ - return yaze_run_cocoa_app_delegate(rom_filename.c_str()); -#endif - - auto controller = std::make_unique(); - EXIT_IF_ERROR(controller->OnEntry(rom_filename)) - while (controller->IsActive()) { - controller->OnInput(); - if (auto status = controller->OnLoad(); !status.ok()) { - LOG_ERROR("App", "Controller OnLoad failed: %s", status.message()); - break; - } - controller->DoRender(); - } - controller->OnExit(); - LOG_INFO("App", "Yaze shutting down."); - return EXIT_SUCCESS; -} diff --git a/src/cli/service/ai/prompt_builder.cc b/src/cli/service/ai/prompt_builder.cc index 97b0a0e7..2ddaeeff 100644 --- a/src/cli/service/ai/prompt_builder.cc +++ b/src/cli/service/ai/prompt_builder.cc @@ -120,10 +120,22 @@ absl::StatusOr PromptBuilder::ResolveCataloguePath( for (const auto& candidate : search_paths) { fs::path resolved = candidate; if (resolved.is_relative()) { - resolved = fs::absolute(resolved); + try { + resolved = fs::absolute(resolved); + } catch (const std::exception& e) { + // If we can't resolve the absolute path (e.g., cwd doesn't exist), + // just try the relative path as-is + continue; + } } - if (fs::exists(resolved)) { - return resolved.string(); + + try { + if (fs::exists(resolved)) { + return resolved.string(); + } + } catch (const std::exception& e) { + // If checking existence fails, just continue to next path + continue; } }