feat: Enhance Agent Editor and Chat Widget for Improved Configuration and Collaboration

- Introduced a comprehensive AI Agent configuration dashboard, allowing users to select providers, models, and adjust settings.
- Updated the AgentChatWidget to include a reactive UI for collaboration status and improved connection management.
- Enhanced the EditorManager to initialize the agent editor with dependencies and streamline the editor selection process.
- Added functionality to clear recent editors upon loading a new ROM, ensuring a fresh start for user sessions.
- Improved the menu structure to include new options for the AI Agent, enhancing user interaction and accessibility.
This commit is contained in:
scawful
2025-10-05 04:03:37 -04:00
parent c495368d6a
commit cbfed441ad
9 changed files with 588 additions and 372 deletions

View File

@@ -490,32 +490,15 @@ void AgentChatWidget::Draw() {
ImGui::SetNextWindowSize(ImVec2(1200, 800), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(1200, 800), ImGuiCond_FirstUseEver);
ImGui::Begin(title_.c_str(), &active_, ImGuiWindowFlags_MenuBar); ImGui::Begin(title_.c_str(), &active_, ImGuiWindowFlags_MenuBar);
// Modern menu bar // Simplified menu bar
if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenuBar()) {
if (ImGui::BeginMenu("File")) { if (ImGui::BeginMenu(ICON_MD_MENU " Actions")) {
if (ImGui::MenuItem(ICON_MD_SAVE " Export History")) {
// TODO: Implement export
}
if (ImGui::MenuItem(ICON_MD_FOLDER_OPEN " Import History")) {
// TODO: Implement import
}
ImGui::Separator();
if (ImGui::MenuItem(ICON_MD_DELETE_FOREVER " Clear History")) { if (ImGui::MenuItem(ICON_MD_DELETE_FOREVER " Clear History")) {
agent_service_.ResetConversation(); agent_service_.ResetConversation();
if (toast_manager_) { if (toast_manager_) {
toast_manager_->Show("Chat history cleared", ToastType::kInfo, 2.5f); toast_manager_->Show("Chat history cleared", ToastType::kInfo, 2.5f);
} }
} }
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Agent")) {
if (ImGui::MenuItem(ICON_MD_SETTINGS " Configure", nullptr, show_agent_config_)) {
show_agent_config_ = !show_agent_config_;
}
if (ImGui::MenuItem(ICON_MD_TERMINAL " Commands", nullptr, show_z3ed_commands_)) {
show_z3ed_commands_ = !show_z3ed_commands_;
}
ImGui::Separator(); ImGui::Separator();
if (ImGui::MenuItem(ICON_MD_REFRESH " Reset Conversation")) { if (ImGui::MenuItem(ICON_MD_REFRESH " Reset Conversation")) {
agent_service_.ResetConversation(); agent_service_.ResetConversation();
@@ -526,290 +509,182 @@ void AgentChatWidget::Draw() {
ImGui::EndMenu(); ImGui::EndMenu();
} }
if (ImGui::BeginMenu("Collaboration")) { if (ImGui::BeginMenu(ICON_MD_VIEW_SIDEBAR " Panels")) {
if (ImGui::MenuItem(ICON_MD_SYNC " ROM Sync", nullptr, show_rom_sync_)) { ImGui::MenuItem(ICON_MD_SETTINGS " Configuration", nullptr, &show_agent_config_);
show_rom_sync_ = !show_rom_sync_; ImGui::MenuItem(ICON_MD_TERMINAL " Commands", nullptr, &show_z3ed_commands_);
} ImGui::MenuItem(ICON_MD_SYNC " ROM Sync", nullptr, &show_rom_sync_);
if (ImGui::MenuItem(ICON_MD_PHOTO_CAMERA " Snapshot Preview", nullptr, show_snapshot_preview_)) { ImGui::MenuItem(ICON_MD_PHOTO_CAMERA " Snapshots", nullptr, &show_snapshot_preview_);
show_snapshot_preview_ = !show_snapshot_preview_;
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Help")) {
if (ImGui::MenuItem(ICON_MD_HELP " Documentation")) {
// TODO: Open docs
}
if (ImGui::MenuItem(ICON_MD_INFO " About")) {
// TODO: Show about dialog
}
ImGui::EndMenu(); ImGui::EndMenu();
} }
ImGui::EndMenuBar(); ImGui::EndMenuBar();
} }
// Modern tabbed interface // Update reactive status color
if (ImGui::BeginTabBar("AgentChatTabs", ImGuiTabBarFlags_None)) { collaboration_status_color_ = collaboration_state_.active ?
// Main Chat Tab - with integrated controls ImVec4(0.133f, 0.545f, 0.133f, 1.0f) : ImVec4(0.6f, 0.6f, 0.6f, 1.0f);
if (ImGui::BeginTabItem(ICON_MD_CHAT " Chat")) {
// Stylish connection status bar with gradient // Connection status bar at top
ImDrawList* draw_list = ImGui::GetWindowDrawList(); ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 bar_start = ImGui::GetCursorScreenPos(); ImVec2 bar_start = ImGui::GetCursorScreenPos();
ImVec2 bar_size(ImGui::GetContentRegionAvail().x, 85); ImVec2 bar_size(ImGui::GetContentRegionAvail().x, 75);
// Gradient background // Gradient background
ImU32 color_top = ImGui::GetColorU32(ImVec4(0.18f, 0.22f, 0.28f, 1.0f)); ImU32 color_top = ImGui::GetColorU32(ImVec4(0.18f, 0.22f, 0.28f, 1.0f));
ImU32 color_bottom = ImGui::GetColorU32(ImVec4(0.12f, 0.16f, 0.22f, 1.0f)); ImU32 color_bottom = ImGui::GetColorU32(ImVec4(0.12f, 0.16f, 0.22f, 1.0f));
draw_list->AddRectFilledMultiColor(bar_start, draw_list->AddRectFilledMultiColor(bar_start,
ImVec2(bar_start.x + bar_size.x, bar_start.y + bar_size.y), ImVec2(bar_start.x + bar_size.x, bar_start.y + bar_size.y),
color_top, color_top, color_bottom, color_bottom); color_top, color_top, color_bottom, color_bottom);
// Colored accent bar based on provider // Colored accent bar based on provider
ImVec4 accent_color = (agent_config_.ai_provider == "ollama") ? ImVec4(0.2f, 0.8f, 0.4f, 1.0f) : ImVec4 accent_color = (agent_config_.ai_provider == "ollama") ? ImVec4(0.2f, 0.8f, 0.4f, 1.0f) :
(agent_config_.ai_provider == "gemini") ? ImVec4(0.196f, 0.6f, 0.8f, 1.0f) : (agent_config_.ai_provider == "gemini") ? ImVec4(0.196f, 0.6f, 0.8f, 1.0f) :
ImVec4(0.6f, 0.6f, 0.6f, 1.0f); ImVec4(0.6f, 0.6f, 0.6f, 1.0f);
draw_list->AddRectFilled(bar_start, ImVec2(bar_start.x + bar_size.x, bar_start.y + 3), draw_list->AddRectFilled(bar_start, ImVec2(bar_start.x + bar_size.x, bar_start.y + 3),
ImGui::GetColorU32(accent_color)); ImGui::GetColorU32(accent_color));
ImGui::BeginChild("ConnectionStatusBar", bar_size, false, ImGuiWindowFlags_NoScrollbar); ImGui::BeginChild("AgentChat_ConnectionBar", bar_size, false, ImGuiWindowFlags_NoScrollbar);
{ ImGui::PushID("ConnectionBar");
ImGui::Spacing(); {
ImGui::Spacing();
// Provider selection and connection status in one row
ImGui::TextColored(accent_color, ICON_MD_SMART_TOY " AI Provider:"); // Provider selection
ImGui::SameLine(); ImGui::TextColored(accent_color, ICON_MD_SMART_TOY " Provider:");
ImGui::SetNextItemWidth(120); ImGui::SameLine();
const char* providers[] = { "Mock", "Ollama", "Gemini" }; ImGui::SetNextItemWidth(110);
int current_provider = (agent_config_.ai_provider == "mock") ? 0 : const char* providers[] = { "Mock", "Ollama", "Gemini" };
(agent_config_.ai_provider == "ollama") ? 1 : 2; int current_provider = (agent_config_.ai_provider == "mock") ? 0 :
if (ImGui::Combo("##chat_provider_combo", &current_provider, providers, 3)) { (agent_config_.ai_provider == "ollama") ? 1 : 2;
agent_config_.ai_provider = (current_provider == 0) ? "mock" : if (ImGui::Combo("##main_provider", &current_provider, providers, 3)) {
(current_provider == 1) ? "ollama" : "gemini"; agent_config_.ai_provider = (current_provider == 0) ? "mock" :
strncpy(agent_config_.provider_buffer, agent_config_.ai_provider.c_str(), (current_provider == 1) ? "ollama" : "gemini";
sizeof(agent_config_.provider_buffer) - 1); }
}
ImGui::SameLine();
ImGui::SameLine(); if (agent_config_.ai_provider != "mock") {
if (agent_config_.ai_provider == "ollama") { ImGui::TextColored(accent_color, ICON_MD_PSYCHOLOGY " Model:");
ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.4f, 1.0f), ICON_MD_LINK " Host:"); ImGui::SameLine();
ImGui::SameLine(); ImGui::SetNextItemWidth(180);
ImGui::SetNextItemWidth(200); ImGui::InputText("##main_model", agent_config_.model_buffer, sizeof(agent_config_.model_buffer));
if (ImGui::InputText("##chat_ollama_host", agent_config_.ollama_host_buffer, }
sizeof(agent_config_.ollama_host_buffer))) {
agent_config_.ollama_host = agent_config_.ollama_host_buffer; ImGui::SameLine();
} ImGui::Checkbox(ICON_MD_VISIBILITY " Reasoning", &agent_config_.show_reasoning);
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.6f, 0.3f, 0.8f)); // Connection status indicator
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.2f, 0.8f, 0.4f, 1.0f)); ImGui::SameLine(ImGui::GetContentRegionAvail().x - 100);
if (ImGui::Button(ICON_MD_REFRESH " Test##test_ollama_conn")) { if (collaboration_state_.active) {
if (toast_manager_) { ImGui::TextColored(collaboration_status_color_, ICON_MD_CHECK_CIRCLE " Session Active");
toast_manager_->Show("Testing Ollama connection...", ToastType::kInfo); } else {
} ImGui::TextDisabled(ICON_MD_CANCEL " No Session");
} }
ImGui::PopStyleColor(2); }
if (ImGui::IsItemHovered()) { ImGui::PopID();
ImGui::SetTooltip("Test connection to Ollama server"); ImGui::EndChild();
}
} else if (agent_config_.ai_provider == "gemini") { ImGui::Spacing();
ImGui::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f), ICON_MD_KEY " API Key:");
ImGui::SameLine(); // Main layout: Chat area (left, 70%) + Control panels (right, 30%)
ImGui::SetNextItemWidth(250); if (ImGui::BeginTable("AgentChat_MainLayout", 2,
if (ImGui::InputText("##chat_gemini_key", agent_config_.gemini_key_buffer, ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) {
sizeof(agent_config_.gemini_key_buffer), ImGui::TableSetupColumn("Chat", ImGuiTableColumnFlags_WidthStretch, 0.7f);
ImGuiInputTextFlags_Password)) { ImGui::TableSetupColumn("Controls", ImGuiTableColumnFlags_WidthStretch, 0.3f);
agent_config_.gemini_api_key = agent_config_.gemini_key_buffer; ImGui::TableNextRow();
}
ImGui::SameLine(); // LEFT: Chat area (emphasized)
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.5f, 0.7f, 0.8f)); ImGui::TableSetColumnIndex(0);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.196f, 0.6f, 0.8f, 1.0f)); ImGui::PushID("ChatColumn");
if (ImGui::Button(ICON_MD_CHECK " Verify##verify_gemini_key")) { RenderHistory();
if (toast_manager_) { RenderInputBox();
toast_manager_->Show("Verifying Gemini API key...", ToastType::kInfo); ImGui::PopID();
}
} // RIGHT: Control panels (collapsible sections)
ImGui::PopStyleColor(2); ImGui::TableSetColumnIndex(1);
} else { ImGui::PushID("ControlsColumn");
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), ImGui::BeginChild("AgentChat_ControlPanels", ImVec2(0, 0), false);
ICON_MD_INFO " Mock mode - no external connection needed");
} // Quick Actions
if (ImGui::CollapsingHeader(ICON_MD_BOLT " Quick Actions", ImGuiTreeNodeFlags_DefaultOpen)) {
// Second row: Model selection and quick settings ImGui::PushID("QuickActions");
ImGui::TextColored(accent_color, ICON_MD_PSYCHOLOGY " Model:"); if (ImGui::Button(ICON_MD_REFRESH " Reset Chat", ImVec2(-1, 0))) {
ImGui::SameLine(); agent_service_.ResetConversation();
ImGui::SetNextItemWidth(200); if (toast_manager_) {
ImGui::InputText("##chat_model_input", agent_config_.model_buffer, sizeof(agent_config_.model_buffer)); toast_manager_->Show("Conversation reset", ToastType::kInfo);
ImGui::SameLine();
ImGui::Checkbox(ICON_MD_VISIBILITY " Reasoning", &agent_config_.show_reasoning);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Show agent's reasoning process in responses");
}
ImGui::SameLine();
ImGui::TextColored(accent_color, ICON_MD_LOOP);
ImGui::SameLine();
ImGui::SetNextItemWidth(80);
ImGui::InputInt("##chat_max_iter", &agent_config_.max_tool_iterations);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Maximum tool call iterations per request");
} }
} }
ImGui::EndChild(); ImGui::PopID();
ImGui::Spacing();
// Main chat area
RenderHistory();
RenderInputBox();
ImGui::EndTabItem();
} }
// Commands Tab - Z3ED and Multimodal // Collapsible panels
if (ImGui::BeginTabItem(ICON_MD_TERMINAL " Commands")) { if (show_agent_config_) {
RenderZ3EDCommandPanel();
ImGui::Separator();
RenderMultimodalPanel();
ImGui::EndTabItem();
}
// Collaboration Tab
if (ImGui::BeginTabItem(ICON_MD_PEOPLE " Collaboration")) {
RenderCollaborationPanel();
ImGui::Separator();
RenderRomSyncPanel();
if (show_snapshot_preview_) {
ImGui::Separator();
RenderSnapshotPreviewPanel();
}
ImGui::EndTabItem();
}
// Advanced Settings Tab
if (ImGui::BeginTabItem(ICON_MD_SETTINGS " Advanced")) {
RenderAgentConfigPanel(); RenderAgentConfigPanel();
ImGui::EndTabItem();
} }
// Proposals Tab if (show_z3ed_commands_) {
if (ImGui::BeginTabItem(ICON_MD_PREVIEW " Proposals")) { RenderZ3EDCommandPanel();
RenderProposalManagerPanel();
ImGui::EndTabItem();
} }
// Metrics Tab RenderCollaborationPanel();
if (ImGui::BeginTabItem(ICON_MD_ANALYTICS " Metrics")) { RenderMultimodalPanel();
auto metrics = agent_service_.GetMetrics();
ImGui::Text("Session Statistics"); if (show_rom_sync_) {
ImGui::Separator(); RenderRomSyncPanel();
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::TableNextColumn();
ImGui::Text("Total Messages");
ImGui::TableNextColumn();
ImGui::Text("%d user, %d agent", metrics.total_user_messages, metrics.total_agent_messages);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Tool Calls");
ImGui::TableNextColumn();
ImGui::Text("%d", metrics.total_tool_calls);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Commands Executed");
ImGui::TableNextColumn();
ImGui::Text("%d", metrics.total_commands);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Proposals Created");
ImGui::TableNextColumn();
ImGui::Text("%d", metrics.total_proposals);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Average Latency");
ImGui::TableNextColumn();
ImGui::Text("%.2f seconds", metrics.average_latency_seconds);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Total Session Time");
ImGui::TableNextColumn();
ImGui::Text("%.1f seconds", metrics.total_elapsed_seconds);
ImGui::EndTable();
}
ImGui::EndTabItem();
} }
// File Editor Tabs RenderProposalManagerPanel();
if (ImGui::BeginTabItem(ICON_MD_EDIT_NOTE " Files")) {
RenderFileEditorTabs();
ImGui::EndTabItem();
}
// System Prompt Editor Tab ImGui::EndChild();
if (ImGui::BeginTabItem(ICON_MD_SETTINGS_SUGGEST " System Prompt")) { ImGui::PopID();
RenderSystemPromptEditor();
ImGui::EndTabItem();
}
ImGui::EndTabBar(); ImGui::EndTable();
} }
ImGui::End(); ImGui::End();
} }
void AgentChatWidget::RenderCollaborationPanel() { void AgentChatWidget::RenderCollaborationPanel() {
if (!ImGui::CollapsingHeader("Collaborative Session", ImGui::PushID("CollabPanel");
ImGuiTreeNodeFlags_DefaultOpen)) {
return; // Update reactive status color based on connection state
const bool connected = collaboration_state_.active;
collaboration_status_color_ = connected ? ImVec4(0.133f, 0.545f, 0.133f, 1.0f) : ImVec4(0.6f, 0.6f, 0.6f, 1.0f);
// Top section: Mode selector in a styled box
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.15f, 0.18f, 0.22f, 0.8f));
ImGui::BeginChild("Collab_ModeSelector", ImVec2(0, 45), true, ImGuiWindowFlags_NoScrollbar);
{
ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_SETTINGS_ETHERNET " Collaboration Mode:");
ImGui::SameLine();
ImGui::RadioButton(ICON_MD_FOLDER " Local##collab_mode_local",
reinterpret_cast<int*>(&collaboration_state_.mode),
static_cast<int>(CollaborationMode::kLocal));
ImGui::SameLine();
ImGui::RadioButton(ICON_MD_WIFI " Network##collab_mode_network",
reinterpret_cast<int*>(&collaboration_state_.mode),
static_cast<int>(CollaborationMode::kNetwork));
} }
ImGui::EndChild();
ImGui::PopStyleColor();
ImGui::Spacing();
// Mode selector // Main content in table layout
ImGui::Text("Mode:"); if (ImGui::BeginTable("Collab_MainTable", 2, ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_Resizable)) {
ImGui::SameLine(); ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed, 320);
int mode = static_cast<int>(collaboration_state_.mode);
if (ImGui::RadioButton("Local", &mode, 0)) {
collaboration_state_.mode = CollaborationMode::kLocal;
}
ImGui::SameLine();
if (ImGui::RadioButton("Network", &mode, 1)) {
collaboration_state_.mode = CollaborationMode::kNetwork;
}
ImGui::Separator();
// Table layout: Left side = Session Details, Right side = Controls
if (ImGui::BeginTable(
"collab_table", 2,
ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_SizingFixedFit)) {
ImGui::TableSetupColumn("Session Details", ImGuiTableColumnFlags_WidthFixed,
250.0f);
ImGui::TableSetupColumn("Controls", ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("Controls", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextRow(); ImGui::TableNextRow();
// LEFT COLUMN: Session Details with gradient background // LEFT COLUMN: Session Details
ImGui::TableSetColumnIndex(0); ImGui::TableSetColumnIndex(0);
ImGui::BeginGroup();
ImGui::PushID("StatusColumn");
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.15f, 0.2f, 0.18f, 0.4f)); ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.15f, 0.2f, 0.18f, 0.4f));
ImGui::BeginChild("session_details", ImVec2(0, 180), true); ImGui::BeginChild("Collab_SessionDetails", ImVec2(0, 180), true);
ImVec4 status_color = ImVec4(1.0f, 0.843f, 0.0f, 1.0f);
const bool connected = collaboration_state_.active;
ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_INFO " Session Status:"); ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_INFO " Session Status:");
ImGui::Spacing(); ImGui::Spacing();
if (connected) { if (connected) {
ImGui::TextColored(status_color, ICON_MD_CHECK_CIRCLE " Connected"); ImGui::TextColored(collaboration_status_color_, ICON_MD_CHECK_CIRCLE " Connected");
} else { } else {
ImGui::TextDisabled(ICON_MD_CANCEL " Not connected"); ImGui::TextDisabled(ICON_MD_CANCEL " Not connected");
} }
@@ -822,13 +697,13 @@ void AgentChatWidget::RenderCollaborationPanel() {
if (!collaboration_state_.session_name.empty()) { if (!collaboration_state_.session_name.empty()) {
ImGui::Spacing(); ImGui::Spacing();
ImGui::TextColored(status_color, ICON_MD_LABEL " Session:"); ImGui::TextColored(collaboration_status_color_, ICON_MD_LABEL " Session:");
ImGui::TextWrapped("%s", collaboration_state_.session_name.c_str()); ImGui::TextWrapped("%s", collaboration_state_.session_name.c_str());
} }
if (!collaboration_state_.session_id.empty()) { if (!collaboration_state_.session_id.empty()) {
ImGui::Spacing(); ImGui::Spacing();
ImGui::TextColored(status_color, ICON_MD_KEY " Session Code:"); ImGui::TextColored(collaboration_status_color_, ICON_MD_KEY " Session Code:");
ImGui::TextWrapped("%s", collaboration_state_.session_id.c_str()); ImGui::TextWrapped("%s", collaboration_state_.session_id.c_str());
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.4f, 0.6f, 0.6f)); 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)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.416f, 0.353f, 0.804f, 1.0f));
@@ -848,31 +723,41 @@ void AgentChatWidget::RenderCollaborationPanel() {
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
"%s", absl::FormatTime("%H:%M:%S", collaboration_state_.last_synced, "%s", absl::FormatTime("%H:%M:%S", collaboration_state_.last_synced,
absl::LocalTimeZone()) absl::LocalTimeZone())
.c_str()); .c_str());
} }
ImGui::EndChild(); ImGui::EndChild();
ImGui::PopStyleColor(); ImGui::PopStyleColor();
// Show participants list below session details ImGui::Spacing();
// Participants list below session details
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.12f, 0.16f, 0.14f, 0.4f)); ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.12f, 0.16f, 0.14f, 0.4f));
ImGui::BeginChild("participants", ImVec2(0, 0), true); ImGui::BeginChild("Collab_ParticipantsList", ImVec2(0, 0), true);
if (collaboration_state_.participants.empty()) { {
ImGui::TextDisabled(ICON_MD_PEOPLE " No participants yet"); if (collaboration_state_.participants.empty()) {
} else { ImGui::TextDisabled(ICON_MD_PEOPLE " No participants yet");
ImGui::TextColored(status_color, ICON_MD_PEOPLE " Participants (%zu):", } else {
collaboration_state_.participants.size()); ImGui::TextColored(collaboration_status_color_, ICON_MD_PEOPLE " Participants (%zu):",
ImGui::Separator(); collaboration_state_.participants.size());
for (const auto& participant : collaboration_state_.participants) { ImGui::Separator();
ImGui::BulletText(ICON_MD_PERSON " %s", participant.c_str()); for (size_t i = 0; i < collaboration_state_.participants.size(); ++i) {
ImGui::PushID(static_cast<int>(i));
ImGui::BulletText(ICON_MD_PERSON " %s", collaboration_state_.participants[i].c_str());
ImGui::PopID();
}
} }
} }
ImGui::EndChild(); ImGui::EndChild();
ImGui::PopStyleColor(); ImGui::PopStyleColor();
ImGui::PopID(); // StatusColumn
ImGui::EndGroup();
// RIGHT COLUMN: Controls // RIGHT COLUMN: Controls
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
ImGui::BeginChild("controls", ImVec2(0, 0), false); ImGui::BeginGroup();
ImGui::PushID("ControlsColumn");
ImGui::BeginChild("Collab_Controls", ImVec2(0, 0), false);
ImGui::Separator(); ImGui::Separator();
@@ -1004,7 +889,7 @@ void AgentChatWidget::RenderCollaborationPanel() {
ImGui::SetTooltip("Join an existing collaboration session"); ImGui::SetTooltip("Join an existing collaboration session");
} }
if (connected) { if (collaboration_state_.active) {
ImGui::Separator(); ImGui::Separator();
ImGui::Spacing(); ImGui::Spacing();
@@ -1030,11 +915,10 @@ void AgentChatWidget::RenderCollaborationPanel() {
ToastType::kError, 5.0f); ToastType::kError, 5.0f);
} }
} }
ImGui::PopStyleColor(2);
if (!can_leave) if (!can_leave)
ImGui::EndDisabled(); ImGui::EndDisabled();
}
if (connected) {
ImGui::Spacing(); ImGui::Spacing();
ImGui::Separator(); ImGui::Separator();
if (!can_refresh) if (!can_refresh)
@@ -1052,48 +936,63 @@ void AgentChatWidget::RenderCollaborationPanel() {
ImGui::EndDisabled(); ImGui::EndDisabled();
} else { } else {
ImGui::Spacing(); ImGui::Spacing();
ImGui::TextDisabled("Start or join a session to collaborate."); ImGui::TextDisabled(ICON_MD_INFO " Start or join a session to collaborate.");
} }
ImGui::EndChild(); // controls ImGui::EndChild(); // Collab_Controls
ImGui::PopID(); // ControlsColumn
ImGui::EndGroup();
ImGui::EndTable(); ImGui::EndTable();
} }
ImGui::PopID(); // CollabPanel
} }
void AgentChatWidget::RenderMultimodalPanel() { void AgentChatWidget::RenderMultimodalPanel() {
ImGui::PushID("MultimodalPanel");
ImVec4 gemini_color = ImVec4(0.196f, 0.6f, 0.8f, 1.0f); ImVec4 gemini_color = ImVec4(0.196f, 0.6f, 0.8f, 1.0f);
if (!ImGui::CollapsingHeader(ICON_MD_CAMERA " Gemini Multimodal Vision", if (!ImGui::CollapsingHeader(ICON_MD_CAMERA " Gemini Multimodal Vision",
ImGuiTreeNodeFlags_DefaultOpen)) { ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::PopID();
return; return;
} }
bool can_capture = static_cast<bool>(multimodal_callbacks_.capture_snapshot); bool can_capture = static_cast<bool>(multimodal_callbacks_.capture_snapshot);
bool can_send = static_cast<bool>(multimodal_callbacks_.send_to_gemini); bool can_send = static_cast<bool>(multimodal_callbacks_.send_to_gemini);
// Capture mode selection with icons // Organize in table layout
ImGui::TextColored(gemini_color, ICON_MD_SCREENSHOT " Capture Mode:"); if (ImGui::BeginTable("Multimodal_Layout", 1, ImGuiTableFlags_None)) {
ImGui::RadioButton(ICON_MD_FULLSCREEN " Full Window##mm_radio_full", ImGui::TableSetupColumn("Content", ImGuiTableColumnFlags_WidthStretch);
reinterpret_cast<int*>(&multimodal_state_.capture_mode), ImGui::TableNextRow();
static_cast<int>(CaptureMode::kFullWindow)); ImGui::TableSetColumnIndex(0);
ImGui::SameLine();
ImGui::RadioButton(ICON_MD_DASHBOARD " Active Editor##mm_radio_active", ImGui::BeginGroup();
reinterpret_cast<int*>(&multimodal_state_.capture_mode),
static_cast<int>(CaptureMode::kActiveEditor)); // Capture mode selection with icons
ImGui::SameLine(); ImGui::TextColored(gemini_color, ICON_MD_SCREENSHOT " Capture Mode:");
ImGui::RadioButton(ICON_MD_WINDOW " Specific##mm_radio_specific", ImGui::RadioButton(ICON_MD_FULLSCREEN " Full Window##mm_radio_full",
reinterpret_cast<int*>(&multimodal_state_.capture_mode), reinterpret_cast<int*>(&multimodal_state_.capture_mode),
static_cast<int>(CaptureMode::kSpecificWindow)); static_cast<int>(CaptureMode::kFullWindow));
ImGui::SameLine();
ImGui::RadioButton(ICON_MD_DASHBOARD " Active Editor##mm_radio_active",
reinterpret_cast<int*>(&multimodal_state_.capture_mode),
static_cast<int>(CaptureMode::kActiveEditor));
ImGui::SameLine();
ImGui::RadioButton(ICON_MD_WINDOW " Specific##mm_radio_specific",
reinterpret_cast<int*>(&multimodal_state_.capture_mode),
static_cast<int>(CaptureMode::kSpecificWindow));
// If specific window mode, show input for window name // If specific window mode, show input for window name
if (multimodal_state_.capture_mode == CaptureMode::kSpecificWindow) { if (multimodal_state_.capture_mode == CaptureMode::kSpecificWindow) {
ImGui::SetNextItemWidth(-1); ImGui::SetNextItemWidth(-1);
ImGui::InputTextWithHint("##mm_window_name_input", "Window name (e.g., Overworld Editor)...", ImGui::InputTextWithHint("##mm_window_name_input", "Window name (e.g., Overworld Editor)...",
multimodal_state_.specific_window_buffer, multimodal_state_.specific_window_buffer,
IM_ARRAYSIZE(multimodal_state_.specific_window_buffer)); IM_ARRAYSIZE(multimodal_state_.specific_window_buffer));
} }
ImGui::Separator(); ImGui::EndGroup();
ImGui::Separator();
if (!can_capture) if (!can_capture)
ImGui::BeginDisabled(); ImGui::BeginDisabled();
@@ -1205,6 +1104,11 @@ void AgentChatWidget::RenderMultimodalPanel() {
.c_str()); .c_str());
} }
} }
ImGui::EndTable();
}
ImGui::PopID(); // MultimodalPanel
} }
void AgentChatWidget::RefreshCollaboration() { void AgentChatWidget::RefreshCollaboration() {
@@ -1427,24 +1331,32 @@ void AgentChatWidget::RenderAgentConfigPanel() {
} }
void AgentChatWidget::RenderZ3EDCommandPanel() { void AgentChatWidget::RenderZ3EDCommandPanel() {
ImGui::PushID("Z3EDCmdPanel");
ImVec4 command_color = ImVec4(1.0f, 0.647f, 0.0f, 1.0f);
if (!ImGui::CollapsingHeader(ICON_MD_TERMINAL " Z3ED Commands", if (!ImGui::CollapsingHeader(ICON_MD_TERMINAL " Z3ED Commands",
ImGuiTreeNodeFlags_DefaultOpen)) { ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::PopID();
return; return;
} }
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.14f, 0.12f, 0.18f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.14f, 0.12f, 0.18f, 1.0f));
ImGui::BeginChild("Z3EDCommands", ImVec2(0, 300), true); ImGui::BeginChild("Z3ED_CommandsChild", ImVec2(0, 300), true);
ImGui::Text(ICON_MD_CODE " Command Palette"); ImGui::TextColored(command_color, ICON_MD_CODE " Command Palette");
ImGui::Separator(); ImGui::Separator();
ImGui::Spacing();
ImGui::InputTextWithHint("##z3ed_command", "Enter z3ed command or prompt...", ImGui::SetNextItemWidth(-1);
ImGui::InputTextWithHint("##z3ed_cmd_input_field", "Enter z3ed command or prompt...",
z3ed_command_state_.command_input_buffer, z3ed_command_state_.command_input_buffer,
IM_ARRAYSIZE(z3ed_command_state_.command_input_buffer)); IM_ARRAYSIZE(z3ed_command_state_.command_input_buffer));
ImGui::BeginDisabled(z3ed_command_state_.command_running); ImGui::BeginDisabled(z3ed_command_state_.command_running);
if (ImGui::Button(ICON_MD_PLAY_ARROW " Run Task", ImVec2(-1, 0)) || ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.6f, 0.4f, 0.0f, 0.8f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, command_color);
if (ImGui::Button(ICON_MD_PLAY_ARROW " Run Task##z3ed_run_task_btn", ImVec2(-1, 0)) ||
(ImGui::IsItemFocused() && ImGui::IsKeyPressed(ImGuiKey_Enter))) { (ImGui::IsItemFocused() && ImGui::IsKeyPressed(ImGuiKey_Enter))) {
if (z3ed_callbacks_.run_agent_task) { if (z3ed_callbacks_.run_agent_task) {
std::string command = z3ed_command_state_.command_input_buffer; std::string command = z3ed_command_state_.command_input_buffer;
@@ -1463,15 +1375,19 @@ void AgentChatWidget::RenderZ3EDCommandPanel() {
} }
} }
ImGui::PopStyleColor(2);
ImGui::EndDisabled(); ImGui::EndDisabled();
ImGui::Spacing(); ImGui::Spacing();
ImGui::Text(ICON_MD_LIST " Quick Actions"); ImGui::TextColored(command_color, ICON_MD_LIST " Quick Actions");
ImGui::Separator(); ImGui::Separator();
ImGui::Spacing();
if (ImGui::BeginTable("Z3EDQuickActions", 2, ImGuiTableFlags_SizingStretchProp)) { if (ImGui::BeginTable("Z3ED_QuickActionsTable", 2, ImGuiTableFlags_SizingStretchProp)) {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (ImGui::Button(ICON_MD_PREVIEW " List Proposals", ImVec2(-1, 0))) { ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.35f, 0.6f, 0.8f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.502f, 0.0f, 0.502f, 1.0f));
if (ImGui::Button(ICON_MD_PREVIEW " List##z3ed_list_btn", ImVec2(-1, 0))) {
if (z3ed_callbacks_.list_proposals) { if (z3ed_callbacks_.list_proposals) {
auto result = z3ed_callbacks_.list_proposals(); auto result = z3ed_callbacks_.list_proposals();
if (result.ok()) { if (result.ok()) {
@@ -1485,9 +1401,12 @@ void AgentChatWidget::RenderZ3EDCommandPanel() {
} }
} }
} }
ImGui::PopStyleColor(2);
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (ImGui::Button(ICON_MD_DIFFERENCE " View Diff", ImVec2(-1, 0))) { 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_DIFFERENCE " Diff##z3ed_diff_btn", ImVec2(-1, 0))) {
if (z3ed_callbacks_.diff_proposal) { if (z3ed_callbacks_.diff_proposal) {
auto result = z3ed_callbacks_.diff_proposal(""); auto result = z3ed_callbacks_.diff_proposal("");
if (result.ok()) { if (result.ok()) {
@@ -1495,18 +1414,24 @@ void AgentChatWidget::RenderZ3EDCommandPanel() {
} }
} }
} }
ImGui::PopStyleColor(2);
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (ImGui::Button(ICON_MD_CHECK " Accept", ImVec2(-1, 0))) { ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.1f, 0.5f, 0.1f, 0.8f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.133f, 0.545f, 0.133f, 1.0f));
if (ImGui::Button(ICON_MD_CHECK " Accept##z3ed_accept_btn", ImVec2(-1, 0))) {
if (z3ed_callbacks_.accept_proposal && proposal_drawer_) { if (z3ed_callbacks_.accept_proposal && proposal_drawer_) {
// TODO: Get selected proposal ID from drawer // TODO: Get selected proposal ID from drawer
auto status = z3ed_callbacks_.accept_proposal(""); auto status = z3ed_callbacks_.accept_proposal("");
(void)status; // Acknowledge result (void)status; // Acknowledge result
} }
} }
ImGui::PopStyleColor(2);
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (ImGui::Button(ICON_MD_CLOSE " Reject", ImVec2(-1, 0))) { 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_CLOSE " Reject##z3ed_reject_btn", ImVec2(-1, 0))) {
if (z3ed_callbacks_.reject_proposal && proposal_drawer_) { if (z3ed_callbacks_.reject_proposal && proposal_drawer_) {
// TODO: Get selected proposal ID from drawer // TODO: Get selected proposal ID from drawer
auto status = z3ed_callbacks_.reject_proposal(""); auto status = z3ed_callbacks_.reject_proposal("");

View File

@@ -278,6 +278,9 @@ public:
bool show_snapshot_preview_ = false; bool show_snapshot_preview_ = false;
std::vector<uint8_t> snapshot_preview_data_; std::vector<uint8_t> snapshot_preview_data_;
// Reactive UI colors
ImVec4 collaboration_status_color_ = ImVec4(0.6f, 0.6f, 0.6f, 1.0f);
// File editing state // File editing state
struct FileEditorTab { struct FileEditorTab {
std::string filepath; std::string filepath;

View File

@@ -9,6 +9,7 @@
#include "app/editor/system/proposal_drawer.h" #include "app/editor/system/proposal_drawer.h"
#include "app/editor/system/toast_manager.h" #include "app/editor/system/toast_manager.h"
#include "app/rom.h" #include "app/rom.h"
#include "app/gui/icons.h"
#ifdef YAZE_WITH_GRPC #ifdef YAZE_WITH_GRPC
#include "app/editor/agent/network_collaboration_coordinator.h" #include "app/editor/agent/network_collaboration_coordinator.h"
@@ -18,20 +19,60 @@ namespace yaze {
namespace editor { namespace editor {
AgentEditor::AgentEditor() { AgentEditor::AgentEditor() {
type_ = EditorType::kAgent;
chat_widget_ = std::make_unique<AgentChatWidget>(); chat_widget_ = std::make_unique<AgentChatWidget>();
local_coordinator_ = std::make_unique<AgentCollaborationCoordinator>(); local_coordinator_ = std::make_unique<AgentCollaborationCoordinator>();
// Initialize default configuration
current_config_.provider = "mock";
current_config_.show_reasoning = true;
current_config_.max_tool_iterations = 4;
} }
AgentEditor::~AgentEditor() = default; AgentEditor::~AgentEditor() = default;
void AgentEditor::Initialize(ToastManager* toast_manager, void AgentEditor::Initialize() {
ProposalDrawer* proposal_drawer) { // Base initialization
}
absl::Status AgentEditor::Load() {
// Load agent configuration from project/settings
// TODO: Load from config file
return absl::OkStatus();
}
absl::Status AgentEditor::Save() {
// Save agent configuration
// TODO: Save to config file
return absl::OkStatus();
}
absl::Status AgentEditor::Update() {
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) {
toast_manager_ = toast_manager; toast_manager_ = toast_manager;
proposal_drawer_ = proposal_drawer; proposal_drawer_ = proposal_drawer;
rom_ = rom;
chat_widget_->SetToastManager(toast_manager_);
chat_widget_->SetProposalDrawer(proposal_drawer_); if (chat_widget_) {
chat_widget_->SetToastManager(toast_manager);
chat_widget_->SetProposalDrawer(proposal_drawer);
if (rom) {
chat_widget_->SetRomContext(rom);
}
}
SetupChatWidgetCallbacks(); SetupChatWidgetCallbacks();
SetupMultimodalCallbacks(); SetupMultimodalCallbacks();
} }
@@ -43,9 +84,194 @@ void AgentEditor::SetRomContext(Rom* rom) {
} }
} }
void AgentEditor::Draw() { void AgentEditor::DrawDashboard() {
if (!active_) return;
ImGui::Begin(ICON_MD_SMART_TOY " AI Agent Configuration", &active_, ImGuiWindowFlags_MenuBar);
// Menu bar
if (ImGui::BeginMenuBar()) {
if (ImGui::BeginMenu(ICON_MD_MENU " Actions")) {
if (ImGui::MenuItem(ICON_MD_CHAT " Open Chat Window", "Ctrl+Shift+A")) {
OpenChatWindow();
}
ImGui::Separator();
if (ImGui::MenuItem(ICON_MD_SAVE " Save Configuration")) {
Save();
}
if (ImGui::MenuItem(ICON_MD_REFRESH " Reset to Defaults")) {
current_config_ = AgentConfig{};
current_config_.show_reasoning = true;
current_config_.max_tool_iterations = 4;
}
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
// Dashboard content in organized sections
if (ImGui::BeginTable("AgentDashboard_Layout", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) {
ImGui::TableSetupColumn("Configuration", ImGuiTableColumnFlags_WidthStretch, 0.6f);
ImGui::TableSetupColumn("Info", ImGuiTableColumnFlags_WidthStretch, 0.4f);
ImGui::TableNextRow();
// LEFT: Configuration
ImGui::TableSetColumnIndex(0);
ImGui::PushID("ConfigColumn");
DrawConfigurationPanel();
ImGui::PopID();
// RIGHT: Status & Info
ImGui::TableSetColumnIndex(1);
ImGui::PushID("InfoColumn");
DrawStatusPanel();
DrawMetricsPanel();
ImGui::PopID();
ImGui::EndTable();
}
ImGui::End();
}
void AgentEditor::DrawConfigurationPanel() {
// 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_config_.provider == "mock");
bool is_ollama = (current_config_.provider == "ollama");
bool is_gemini = (current_config_.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_config_.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_config_.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_config_.provider = "gemini";
}
if (is_gemini) ImGui::PopStyleColor();
ImGui::Separator();
ImGui::Spacing();
// Provider-specific settings
if (current_config_.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";
ImGui::InputTextWithHint("##ollama_model", "e.g., qwen2.5-coder:7b, llama3.2", model_buf, sizeof(model_buf));
current_config_.model = model_buf;
ImGui::Text("Host URL:");
ImGui::SetNextItemWidth(-1);
static char host_buf[256] = "http://localhost:11434";
ImGui::InputText("##ollama_host", host_buf, sizeof(host_buf));
current_config_.ollama_host = host_buf;
} else if (current_config_.provider == "gemini") {
ImGui::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f), ICON_MD_SMART_TOY " Gemini Settings");
ImGui::Text("Model:");
ImGui::SetNextItemWidth(-1);
static char model_buf[128] = "gemini-1.5-flash";
ImGui::InputTextWithHint("##gemini_model", "e.g., gemini-1.5-flash", model_buf, sizeof(model_buf));
current_config_.model = model_buf;
ImGui::Text("API Key:");
ImGui::SetNextItemWidth(-1);
static char key_buf[256] = "";
ImGui::InputText("##gemini_key", key_buf, sizeof(key_buf), ImGuiInputTextFlags_Password);
current_config_.gemini_api_key = key_buf;
} 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", &current_config_.show_reasoning);
ImGui::Checkbox(ICON_MD_ANALYTICS " Verbose Output", &current_config_.verbose);
ImGui::SliderInt(ICON_MD_LOOP " Max Iterations", &current_config_.max_tool_iterations, 1, 10);
}
// 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 Configuration", ImVec2(-1, 40))) {
ApplyConfig(current_config_);
if (toast_manager_) {
toast_manager_->Show("Agent configuration applied", ToastType::kSuccess);
}
}
ImGui::PopStyleColor(2);
}
void AgentEditor::DrawStatusPanel() {
// Chat Status
if (ImGui::CollapsingHeader(ICON_MD_CHAT " Chat Status", ImGuiTreeNodeFlags_DefaultOpen)) {
if (chat_widget_ && chat_widget_->is_active()) {
ImGui::TextColored(ImVec4(0.133f, 0.545f, 0.133f, 1.0f), ICON_MD_CHECK_CIRCLE " Chat Active");
} else {
ImGui::TextDisabled(ICON_MD_CANCEL " Chat Inactive");
}
ImGui::Spacing();
if (ImGui::Button(ICON_MD_OPEN_IN_NEW " Open Chat", ImVec2(-1, 0))) {
OpenChatWindow();
}
}
// Collaboration Status
if (ImGui::CollapsingHeader(ICON_MD_PEOPLE " Collaboration")) {
ImGui::TextDisabled("Mode: %s", current_mode_ == CollaborationMode::kLocal ? "Local" : "Network");
ImGui::TextDisabled(ICON_MD_INFO " Configure in chat window");
}
}
void AgentEditor::DrawMetricsPanel() {
if (ImGui::CollapsingHeader(ICON_MD_ANALYTICS " Session Metrics")) {
if (chat_widget_) {
ImGui::TextDisabled("View metrics in chat window");
} else {
ImGui::TextDisabled("No metrics available");
}
}
}
AgentEditor::AgentConfig AgentEditor::GetCurrentConfig() const {
return current_config_;
}
void AgentEditor::ApplyConfig(const AgentConfig& config) {
current_config_ = config;
// Apply to chat widget if available
if (chat_widget_) { if (chat_widget_) {
chat_widget_->Draw(); AgentChatWidget::AgentConfigState chat_config;
chat_config.ai_provider = config.provider;
chat_config.ai_model = config.model;
chat_config.ollama_host = config.ollama_host;
chat_config.gemini_api_key = config.gemini_api_key;
chat_config.verbose = config.verbose;
chat_config.show_reasoning = config.show_reasoning;
chat_config.max_tool_iterations = config.max_tool_iterations;
chat_widget_->UpdateAgentConfig(chat_config);
} }
} }
@@ -63,6 +289,12 @@ void AgentEditor::ToggleChat() {
SetChatActive(!IsChatActive()); SetChatActive(!IsChatActive());
} }
void AgentEditor::OpenChatWindow() {
if (chat_widget_) {
chat_widget_->set_active(true);
}
}
absl::StatusOr<AgentEditor::SessionInfo> AgentEditor::HostSession( absl::StatusOr<AgentEditor::SessionInfo> AgentEditor::HostSession(
const std::string& session_name, CollaborationMode mode) { const std::string& session_name, CollaborationMode mode) {
current_mode_ = mode; current_mode_ = mode;

View File

@@ -8,6 +8,7 @@
#include "absl/status/status.h" #include "absl/status/status.h"
#include "absl/status/statusor.h" #include "absl/status/statusor.h"
#include "app/editor/editor.h"
namespace yaze { namespace yaze {
@@ -26,33 +27,68 @@ class NetworkCollaborationCoordinator;
/** /**
* @class AgentEditor * @class AgentEditor
* @brief Manages all agent-related functionality including chat, collaboration, * @brief AI Agent Configuration Dashboard (separate from chat)
* and network coordination for the Z3ED editor.
* *
* This class serves as a high-level manager for: * A comprehensive configuration editor for the AI agent:
* - Agent chat widget and conversations * - Agent provider configuration (Ollama, Gemini, Mock)
* - Local filesystem-based collaboration * - Model selection and parameters
* - Network-based (WebSocket) collaboration * - Collaboration settings (Local/Network)
* - Coordination between multiple collaboration modes * - Z3ED command automation presets
* - Multimodal/vision configuration
* - Session metrics and monitoring
* - System prompt customization
*
* The chat widget is separate and managed by EditorManager.
*/ */
class AgentEditor { class AgentEditor : public Editor {
public: public:
AgentEditor(); AgentEditor();
~AgentEditor(); ~AgentEditor() override;
// Initialization // Editor interface implementation
void Initialize(ToastManager* toast_manager, ProposalDrawer* proposal_drawer); void Initialize() override;
absl::Status Load() override;
absl::Status Save() override;
absl::Status Update() override;
absl::Status Cut() override { return absl::UnimplementedError("Not applicable"); }
absl::Status Copy() override { return absl::UnimplementedError("Not applicable"); }
absl::Status Paste() override { return absl::UnimplementedError("Not applicable"); }
absl::Status Undo() override { return absl::UnimplementedError("Not applicable"); }
absl::Status Redo() override { return absl::UnimplementedError("Not applicable"); }
absl::Status Find() override { return absl::UnimplementedError("Not applicable"); }
// Initialization with dependencies
void InitializeWithDependencies(ToastManager* toast_manager,
ProposalDrawer* proposal_drawer,
Rom* rom);
void SetRomContext(Rom* rom); void SetRomContext(Rom* rom);
// Main rendering
void Draw();
// Chat widget access // Main rendering (called by Update())
void DrawDashboard();
// Get current agent configuration (for chat to use)
struct AgentConfig {
std::string provider = "mock";
std::string model;
std::string ollama_host = "http://localhost:11434";
std::string gemini_api_key;
bool verbose = false;
bool show_reasoning = true;
int max_tool_iterations = 4;
};
AgentConfig GetCurrentConfig() const;
void ApplyConfig(const AgentConfig& config);
// Chat widget access (for EditorManager)
AgentChatWidget* GetChatWidget() { return chat_widget_.get(); }
bool IsChatActive() const; bool IsChatActive() const;
void SetChatActive(bool active); void SetChatActive(bool active);
void ToggleChat(); void ToggleChat();
void OpenChatWindow();
// Collaboration management
// Collaboration and session management
enum class CollaborationMode { enum class CollaborationMode {
kLocal, // Filesystem-based collaboration kLocal, // Filesystem-based collaboration
kNetwork // WebSocket-based collaboration kNetwork // WebSocket-based collaboration
@@ -64,21 +100,13 @@ class AgentEditor {
std::vector<std::string> participants; std::vector<std::string> participants;
}; };
// Host a new collaboration session
absl::StatusOr<SessionInfo> HostSession(const std::string& session_name, absl::StatusOr<SessionInfo> HostSession(const std::string& session_name,
CollaborationMode mode = CollaborationMode::kLocal); CollaborationMode mode = CollaborationMode::kLocal);
// Join an existing collaboration session
absl::StatusOr<SessionInfo> JoinSession(const std::string& session_code, absl::StatusOr<SessionInfo> JoinSession(const std::string& session_code,
CollaborationMode mode = CollaborationMode::kLocal); CollaborationMode mode = CollaborationMode::kLocal);
// Leave the current collaboration session
absl::Status LeaveSession(); absl::Status LeaveSession();
// Refresh session information
absl::StatusOr<SessionInfo> RefreshSession(); absl::StatusOr<SessionInfo> RefreshSession();
// Multimodal (vision/screenshot) support
struct CaptureConfig { struct CaptureConfig {
enum class CaptureMode { enum class CaptureMode {
kFullWindow, kFullWindow,
@@ -94,32 +122,34 @@ class AgentEditor {
absl::Status SendToGemini(const std::filesystem::path& image_path, absl::Status SendToGemini(const std::filesystem::path& image_path,
const std::string& prompt); const std::string& prompt);
// Server management for network mode
#ifdef YAZE_WITH_GRPC #ifdef YAZE_WITH_GRPC
absl::Status ConnectToServer(const std::string& server_url); absl::Status ConnectToServer(const std::string& server_url);
void DisconnectFromServer(); void DisconnectFromServer();
bool IsConnectedToServer() const; bool IsConnectedToServer() const;
#endif #endif
// State queries
bool IsInSession() const; bool IsInSession() const;
CollaborationMode GetCurrentMode() const; CollaborationMode GetCurrentMode() const;
std::optional<SessionInfo> GetCurrentSession() const; std::optional<SessionInfo> GetCurrentSession() const;
// Access to underlying components (for advanced use) // Access to underlying components
AgentChatWidget* GetChatWidget() { return chat_widget_.get(); }
AgentCollaborationCoordinator* GetLocalCoordinator() { return local_coordinator_.get(); } AgentCollaborationCoordinator* GetLocalCoordinator() { return local_coordinator_.get(); }
#ifdef YAZE_WITH_GRPC #ifdef YAZE_WITH_GRPC
NetworkCollaborationCoordinator* GetNetworkCoordinator() { return network_coordinator_.get(); } NetworkCollaborationCoordinator* GetNetworkCoordinator() { return network_coordinator_.get(); }
#endif #endif
private: private:
// Setup callbacks for the chat widget // Dashboard panel rendering
void DrawConfigurationPanel();
void DrawStatusPanel();
void DrawMetricsPanel();
// Setup callbacks
void SetupChatWidgetCallbacks(); void SetupChatWidgetCallbacks();
void SetupMultimodalCallbacks(); void SetupMultimodalCallbacks();
// Internal state // Internal state
std::unique_ptr<AgentChatWidget> chat_widget_; std::unique_ptr<AgentChatWidget> chat_widget_; // Owned by AgentEditor
std::unique_ptr<AgentCollaborationCoordinator> local_coordinator_; std::unique_ptr<AgentCollaborationCoordinator> local_coordinator_;
#ifdef YAZE_WITH_GRPC #ifdef YAZE_WITH_GRPC
std::unique_ptr<NetworkCollaborationCoordinator> network_coordinator_; std::unique_ptr<NetworkCollaborationCoordinator> network_coordinator_;
@@ -129,11 +159,16 @@ class AgentEditor {
ProposalDrawer* proposal_drawer_ = nullptr; ProposalDrawer* proposal_drawer_ = nullptr;
Rom* rom_ = nullptr; Rom* rom_ = nullptr;
// Configuration state
AgentConfig current_config_;
CollaborationMode current_mode_ = CollaborationMode::kLocal; CollaborationMode current_mode_ = CollaborationMode::kLocal;
bool in_session_ = false; bool in_session_ = false;
std::string current_session_id_; std::string current_session_id_;
std::string current_session_name_; std::string current_session_name_;
std::vector<std::string> current_participants_; std::vector<std::string> current_participants_;
// UI state
bool show_advanced_settings_ = false;
}; };
} // namespace editor } // namespace editor

View File

@@ -61,12 +61,13 @@ enum class EditorType {
kSprite, kSprite,
kMessage, kMessage,
kHex, kHex,
kAgent,
kSettings, kSettings,
}; };
constexpr std::array<const char*, 11> kEditorNames = { constexpr std::array<const char*, 12> kEditorNames = {
"Assembly", "Dungeon", "Graphics", "Music", "Overworld", "Assembly", "Dungeon", "Graphics", "Music", "Overworld",
"Palette", "Screen", "Sprite", "Message", "Hex", "Settings", "Palette", "Screen", "Sprite", "Message", "Hex", "Agent", "Settings",
}; };
/** /**

View File

@@ -237,8 +237,10 @@ void EditorManager::Initialize(const std::string& filename) {
project_file_editor_.SetToastManager(&toast_manager_); project_file_editor_.SetToastManager(&toast_manager_);
#ifdef YAZE_WITH_GRPC #ifdef YAZE_WITH_GRPC
// Initialize the agent editor // Initialize the agent editor as a proper Editor (configuration dashboard)
agent_editor_.Initialize(&toast_manager_, &proposal_drawer_); agent_editor_.set_context(&context_);
agent_editor_.Initialize();
agent_editor_.InitializeWithDependencies(&toast_manager_, &proposal_drawer_, nullptr);
// Set up multimodal (vision) callbacks for Gemini // Set up multimodal (vision) callbacks for Gemini
AgentChatWidget::MultimodalCallbacks multimodal_callbacks; AgentChatWidget::MultimodalCallbacks multimodal_callbacks;
@@ -550,7 +552,7 @@ absl::Status EditorManager::Update() {
#ifdef YAZE_WITH_GRPC #ifdef YAZE_WITH_GRPC
// Draw agent editor (includes chat widget and collaboration UI) // Draw agent editor (includes chat widget and collaboration UI)
agent_editor_.Draw(); agent_editor_.Update();
#endif #endif
// Draw background grid effects for the entire viewport // Draw background grid effects for the entire viewport
@@ -711,11 +713,10 @@ absl::Status EditorManager::Update() {
proposal_drawer_.Draw(); proposal_drawer_.Draw();
} }
#ifdef YAZE_WITH_GRPC #ifdef YAZE_WITH_GRPC
Rom* rom_context = // Update ROM context for agent editor
(current_rom_ != nullptr && current_rom_->is_loaded()) ? current_rom_ if (current_rom_ && current_rom_->is_loaded()) {
: nullptr; agent_editor_.SetRomContext(current_rom_);
agent_editor_.SetRomContext(rom_context); }
agent_editor_.Draw();
#endif #endif
return absl::OkStatus(); return absl::OkStatus();
@@ -858,6 +859,10 @@ void EditorManager::BuildModernMenu() {
[this]() { current_editor_set_->screen_editor_.set_active(true); }, "Ctrl+8") [this]() { current_editor_set_->screen_editor_.set_active(true); }, "Ctrl+8")
.Item("Hex Editor", ICON_MD_DATA_ARRAY, .Item("Hex Editor", ICON_MD_DATA_ARRAY,
[this]() { show_memory_editor_ = true; }, "Ctrl+0") [this]() { show_memory_editor_ = true; }, "Ctrl+0")
#ifdef YAZE_WITH_GRPC
.Item("AI Agent", ICON_MD_SMART_TOY,
[this]() { agent_editor_.set_active(true); }, "Ctrl+Shift+A")
#endif
.Separator() .Separator()
.Item("Welcome Screen", ICON_MD_HOME, .Item("Welcome Screen", ICON_MD_HOME,
[this]() { show_welcome_screen_ = true; }) [this]() { show_welcome_screen_ = true; })
@@ -1856,7 +1861,8 @@ absl::Status EditorManager::LoadRom() {
// Hide welcome screen when ROM is successfully loaded - don't reset manual close state // Hide welcome screen when ROM is successfully loaded - don't reset manual close state
show_welcome_screen_ = false; show_welcome_screen_ = false;
// Show editor selection dialog after ROM loads // Clear recent editors for fresh start with new ROM and show editor selection dialog
editor_selection_dialog_.ClearRecentEditors();
show_editor_selection_ = true; show_editor_selection_ = true;
return absl::OkStatus(); return absl::OkStatus();
@@ -1982,6 +1988,7 @@ absl::Status EditorManager::OpenRomOrProject(const std::string& filename) {
// Hide welcome screen and show editor selection when ROM is loaded // Hide welcome screen and show editor selection when ROM is loaded
show_welcome_screen_ = false; show_welcome_screen_ = false;
editor_selection_dialog_.ClearRecentEditors();
show_editor_selection_ = true; show_editor_selection_ = true;
} }
return absl::OkStatus(); return absl::OkStatus();
@@ -2061,6 +2068,7 @@ absl::Status EditorManager::OpenProject() {
// Hide welcome screen and show editor selection when project ROM is loaded // Hide welcome screen and show editor selection when project ROM is loaded
show_welcome_screen_ = false; show_welcome_screen_ = false;
editor_selection_dialog_.ClearRecentEditors();
show_editor_selection_ = true; show_editor_selection_ = true;
} }

View File

@@ -55,6 +55,10 @@ EditorSelectionDialog::EditorSelectionDialog() {
{EditorType::kHex, "Hex Editor", ICON_MD_DATA_ARRAY, {EditorType::kHex, "Hex Editor", ICON_MD_DATA_ARRAY,
"Direct ROM memory editing and comparison", "Ctrl+0", false, true, "Direct ROM memory editing and comparison", "Ctrl+0", false, true,
ImVec4(0.2f, 0.8f, 0.4f, 1.0f)}, // Matrix green ImVec4(0.2f, 0.8f, 0.4f, 1.0f)}, // Matrix green
{EditorType::kAgent, "AI Agent", ICON_MD_SMART_TOY,
"Configure AI agent, collaboration, and automation", "Ctrl+Shift+A", false, false,
ImVec4(0.8f, 0.4f, 1.0f, 1.0f)}, // Purple/magenta
{EditorType::kSettings, "Settings", ICON_MD_SETTINGS, {EditorType::kSettings, "Settings", ICON_MD_SETTINGS,
"Configure ROM and project settings", "", false, true, "Configure ROM and project settings", "", false, true,

View File

@@ -85,6 +85,14 @@ class EditorSelectionDialog {
*/ */
void SaveRecentEditors(); void SaveRecentEditors();
/**
* @brief Clear recent editors (for new ROM sessions)
*/
void ClearRecentEditors() {
recent_editors_.clear();
SaveRecentEditors();
}
private: private:
void DrawEditorCard(const EditorInfo& info, int index); void DrawEditorCard(const EditorInfo& info, int index);
void DrawWelcomeHeader(); void DrawWelcomeHeader();

View File

@@ -552,7 +552,7 @@ void WelcomeScreen::DrawRecentProjects() {
} }
// Grid layout for project cards (compact) // Grid layout for project cards (compact)
float card_width = 200.0f; // Reduced for compactness float card_width = 220.0f; // Reduced for compactness
float card_height = 95.0f; // Reduced for less scrolling float card_height = 95.0f; // Reduced for less scrolling
int columns = std::max(1, (int)(ImGui::GetContentRegionAvail().x / (card_width + 12))); int columns = std::max(1, (int)(ImGui::GetContentRegionAvail().x / (card_width + 12)));
@@ -650,8 +650,8 @@ void WelcomeScreen::DrawProjectCard(const RecentProject& project, int index) {
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 4, content_pos.y + 58)); ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 4, content_pos.y + 58));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.4f, 0.4f, 0.4f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.4f, 0.4f, 0.4f, 1.0f));
std::string short_path = project.filepath; std::string short_path = project.filepath;
if (short_path.length() > 28) { if (short_path.length() > 26) {
short_path = "..." + short_path.substr(short_path.length() - 25); short_path = "..." + short_path.substr(short_path.length() - 23);
} }
ImGui::Text(ICON_MD_FOLDER " %s", short_path.c_str()); ImGui::Text(ICON_MD_FOLDER " %s", short_path.c_str());
ImGui::PopStyleColor(); ImGui::PopStyleColor();