feat: Revamp AgentChatWidget with modern UI and enhanced functionality
- Introduced a modern menu bar and tabbed interface for improved user experience. - Added agent configuration options, including AI provider selection and behavior settings. - Implemented Z3ED command panel for executing commands and managing proposals. - Enhanced collaboration features with ROM synchronization, snapshot sharing, and proposal management. - Updated metrics display for session statistics and improved toast notifications for user feedback. - Refactored code for better organization and maintainability.
This commit is contained in:
@@ -485,26 +485,178 @@ void AgentChatWidget::Draw() {
|
||||
// Poll for new messages in collaborative sessions
|
||||
PollSharedHistory();
|
||||
|
||||
ImGui::Begin(title_.c_str(), &active_);
|
||||
if (ImGui::BeginTable("#agent_chat_table", 2,
|
||||
ImGuiTableFlags_BordersInnerV |
|
||||
ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_SizingStretchProp)) {
|
||||
ImGui::TableSetupColumn("Session Details", ImGuiTableColumnFlags_WidthFixed,
|
||||
450.0f);
|
||||
ImGui::TableSetupColumn("Chat History", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
RenderMultimodalPanel();
|
||||
RenderCollaborationPanel();
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
RenderHistory();
|
||||
RenderInputBox();
|
||||
ImGui::EndTable();
|
||||
ImGui::SetNextWindowSize(ImVec2(1200, 800), ImGuiCond_FirstUseEver);
|
||||
ImGui::Begin(title_.c_str(), &active_, ImGuiWindowFlags_MenuBar);
|
||||
|
||||
// Modern menu bar
|
||||
if (ImGui::BeginMenuBar()) {
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
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")) {
|
||||
agent_service_.ResetConversation();
|
||||
if (toast_manager_) {
|
||||
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();
|
||||
if (ImGui::MenuItem(ICON_MD_REFRESH " Reset Conversation")) {
|
||||
agent_service_.ResetConversation();
|
||||
if (toast_manager_) {
|
||||
toast_manager_->Show("Conversation reset", ToastType::kInfo, 2.5f);
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Collaboration")) {
|
||||
if (ImGui::MenuItem(ICON_MD_SYNC " ROM Sync", nullptr, show_rom_sync_)) {
|
||||
show_rom_sync_ = !show_rom_sync_;
|
||||
}
|
||||
if (ImGui::MenuItem(ICON_MD_PHOTO_CAMERA " Snapshot Preview", 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::EndMenuBar();
|
||||
}
|
||||
|
||||
// Modern tabbed interface
|
||||
if (ImGui::BeginTabBar("AgentChatTabs", ImGuiTabBarFlags_None)) {
|
||||
// Main Chat Tab
|
||||
if (ImGui::BeginTabItem(ICON_MD_CHAT " Chat")) {
|
||||
if (ImGui::BeginTable("#agent_chat_table", 2,
|
||||
ImGuiTableFlags_BordersInnerV |
|
||||
ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_SizingStretchProp)) {
|
||||
ImGui::TableSetupColumn("Panels", ImGuiTableColumnFlags_WidthFixed, 400.0f);
|
||||
ImGui::TableSetupColumn("Conversation", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::BeginChild("LeftPanels", ImVec2(0, 0), false);
|
||||
|
||||
if (show_agent_config_) {
|
||||
RenderAgentConfigPanel();
|
||||
}
|
||||
RenderMultimodalPanel();
|
||||
if (show_z3ed_commands_) {
|
||||
RenderZ3EDCommandPanel();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
RenderHistory();
|
||||
RenderInputBox();
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
// Collaboration Tab
|
||||
if (ImGui::BeginTabItem(ICON_MD_PEOPLE " Collaboration")) {
|
||||
RenderCollaborationPanel();
|
||||
if (show_rom_sync_) {
|
||||
ImGui::Separator();
|
||||
RenderRomSyncPanel();
|
||||
}
|
||||
if (show_snapshot_preview_) {
|
||||
ImGui::Separator();
|
||||
RenderSnapshotPreviewPanel();
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
// Proposals Tab
|
||||
if (ImGui::BeginTabItem(ICON_MD_PREVIEW " Proposals")) {
|
||||
RenderProposalManagerPanel();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
// Metrics Tab
|
||||
if (ImGui::BeginTabItem(ICON_MD_ANALYTICS " Metrics")) {
|
||||
auto metrics = agent_service_.GetMetrics();
|
||||
ImGui::Text("Session Statistics");
|
||||
ImGui::Separator();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
@@ -1021,5 +1173,460 @@ void AgentChatWidget::PollSharedHistory() {
|
||||
}
|
||||
}
|
||||
|
||||
void AgentChatWidget::UpdateAgentConfig(const AgentConfigState& config) {
|
||||
agent_config_ = config;
|
||||
|
||||
// Apply configuration to the agent service
|
||||
cli::agent::AgentConfig service_config;
|
||||
service_config.verbose = config.verbose;
|
||||
service_config.show_reasoning = config.show_reasoning;
|
||||
service_config.max_tool_iterations = config.max_tool_iterations;
|
||||
service_config.max_retry_attempts = config.max_retry_attempts;
|
||||
|
||||
agent_service_.SetConfig(service_config);
|
||||
|
||||
if (toast_manager_) {
|
||||
toast_manager_->Show("Agent configuration updated", ToastType::kSuccess, 2.5f);
|
||||
}
|
||||
}
|
||||
|
||||
void AgentChatWidget::RenderAgentConfigPanel() {
|
||||
if (!ImGui::CollapsingHeader(ICON_MD_SETTINGS " Agent Configuration",
|
||||
ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.12f, 0.14f, 0.18f, 1.0f));
|
||||
ImGui::BeginChild("AgentConfig", ImVec2(0, 350), true);
|
||||
|
||||
ImGui::Text(ICON_MD_SMART_TOY " AI Provider");
|
||||
ImGui::Separator();
|
||||
|
||||
// Provider selection
|
||||
int provider_idx = 0;
|
||||
if (agent_config_.ai_provider == "ollama") provider_idx = 1;
|
||||
else if (agent_config_.ai_provider == "gemini") provider_idx = 2;
|
||||
|
||||
if (ImGui::RadioButton("Mock (Testing)", &provider_idx, 0)) {
|
||||
agent_config_.ai_provider = "mock";
|
||||
std::snprintf(agent_config_.provider_buffer, sizeof(agent_config_.provider_buffer), "mock");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Ollama (Local)", &provider_idx, 1)) {
|
||||
agent_config_.ai_provider = "ollama";
|
||||
std::snprintf(agent_config_.provider_buffer, sizeof(agent_config_.provider_buffer), "ollama");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Gemini (Cloud)", &provider_idx, 2)) {
|
||||
agent_config_.ai_provider = "gemini";
|
||||
std::snprintf(agent_config_.provider_buffer, sizeof(agent_config_.provider_buffer), "gemini");
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
// Provider-specific settings
|
||||
if (agent_config_.ai_provider == "ollama") {
|
||||
ImGui::Text(ICON_MD_COMPUTER " Ollama Settings");
|
||||
ImGui::InputText("Model", agent_config_.model_buffer, IM_ARRAYSIZE(agent_config_.model_buffer));
|
||||
ImGui::TextDisabled("Example: qwen2.5-coder:7b, llama3.2:3b");
|
||||
ImGui::InputText("Host", agent_config_.ollama_host_buffer, IM_ARRAYSIZE(agent_config_.ollama_host_buffer));
|
||||
|
||||
} else if (agent_config_.ai_provider == "gemini") {
|
||||
ImGui::Text(ICON_MD_CLOUD " Gemini Settings");
|
||||
ImGui::InputText("Model", agent_config_.model_buffer, IM_ARRAYSIZE(agent_config_.model_buffer));
|
||||
ImGui::TextDisabled("Example: gemini-1.5-flash, gemini-2.5-flash");
|
||||
ImGui::InputText("API Key", agent_config_.gemini_key_buffer,
|
||||
IM_ARRAYSIZE(agent_config_.gemini_key_buffer),
|
||||
ImGuiInputTextFlags_Password);
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Text(ICON_MD_TUNE " Behavior Settings");
|
||||
|
||||
ImGui::Checkbox("Verbose Output", &agent_config_.verbose);
|
||||
ImGui::Checkbox("Show Reasoning", &agent_config_.show_reasoning);
|
||||
ImGui::SliderInt("Max Tool Iterations", &agent_config_.max_tool_iterations, 1, 10);
|
||||
ImGui::SliderInt("Max Retry Attempts", &agent_config_.max_retry_attempts, 1, 5);
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
if (ImGui::Button(ICON_MD_CHECK " Apply Configuration", ImVec2(-1, 0))) {
|
||||
agent_config_.ai_model = agent_config_.model_buffer;
|
||||
agent_config_.ollama_host = agent_config_.ollama_host_buffer;
|
||||
agent_config_.gemini_api_key = agent_config_.gemini_key_buffer;
|
||||
UpdateAgentConfig(agent_config_);
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
void AgentChatWidget::RenderZ3EDCommandPanel() {
|
||||
if (!ImGui::CollapsingHeader(ICON_MD_TERMINAL " Z3ED Commands",
|
||||
ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.14f, 0.12f, 0.18f, 1.0f));
|
||||
ImGui::BeginChild("Z3EDCommands", ImVec2(0, 300), true);
|
||||
|
||||
ImGui::Text(ICON_MD_CODE " Command Palette");
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::InputTextWithHint("##z3ed_command", "Enter z3ed command or prompt...",
|
||||
z3ed_command_state_.command_input_buffer,
|
||||
IM_ARRAYSIZE(z3ed_command_state_.command_input_buffer));
|
||||
|
||||
ImGui::BeginDisabled(z3ed_command_state_.command_running);
|
||||
|
||||
if (ImGui::Button(ICON_MD_PLAY_ARROW " Run Task", ImVec2(-1, 0)) ||
|
||||
(ImGui::IsItemFocused() && ImGui::IsKeyPressed(ImGuiKey_Enter))) {
|
||||
if (z3ed_callbacks_.run_agent_task) {
|
||||
std::string command = z3ed_command_state_.command_input_buffer;
|
||||
z3ed_command_state_.command_running = true;
|
||||
auto status = z3ed_callbacks_.run_agent_task(command);
|
||||
z3ed_command_state_.command_running = false;
|
||||
|
||||
if (status.ok() && toast_manager_) {
|
||||
toast_manager_->Show(ICON_MD_CHECK_CIRCLE " Task started",
|
||||
ToastType::kSuccess, 3.0f);
|
||||
} else if (toast_manager_) {
|
||||
toast_manager_->Show(
|
||||
absl::StrFormat(ICON_MD_ERROR " Task failed: %s", status.message()),
|
||||
ToastType::kError, 5.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Text(ICON_MD_LIST " Quick Actions");
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::BeginTable("Z3EDQuickActions", 2, ImGuiTableFlags_SizingStretchProp)) {
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(ICON_MD_PREVIEW " List Proposals", ImVec2(-1, 0))) {
|
||||
if (z3ed_callbacks_.list_proposals) {
|
||||
auto result = z3ed_callbacks_.list_proposals();
|
||||
if (result.ok()) {
|
||||
const auto& proposals = *result;
|
||||
std::string output;
|
||||
for (size_t i = 0; i < proposals.size(); ++i) {
|
||||
if (i > 0) output += "\n";
|
||||
output += proposals[i];
|
||||
}
|
||||
z3ed_command_state_.command_output = output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(ICON_MD_DIFFERENCE " View Diff", ImVec2(-1, 0))) {
|
||||
if (z3ed_callbacks_.diff_proposal) {
|
||||
auto result = z3ed_callbacks_.diff_proposal("");
|
||||
if (result.ok()) {
|
||||
z3ed_command_state_.command_output = *result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(ICON_MD_CHECK " Accept", ImVec2(-1, 0))) {
|
||||
if (z3ed_callbacks_.accept_proposal && proposal_drawer_) {
|
||||
// TODO: Get selected proposal ID from drawer
|
||||
auto status = z3ed_callbacks_.accept_proposal("");
|
||||
(void)status; // Acknowledge result
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(ICON_MD_CLOSE " Reject", ImVec2(-1, 0))) {
|
||||
if (z3ed_callbacks_.reject_proposal && proposal_drawer_) {
|
||||
// TODO: Get selected proposal ID from drawer
|
||||
auto status = z3ed_callbacks_.reject_proposal("");
|
||||
(void)status; // Acknowledge result
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if (!z3ed_command_state_.command_output.empty()) {
|
||||
ImGui::Spacing();
|
||||
ImGui::Text(ICON_MD_TERMINAL " Output");
|
||||
ImGui::Separator();
|
||||
ImGui::BeginChild("CommandOutput", ImVec2(0, 100), true,
|
||||
ImGuiWindowFlags_HorizontalScrollbar);
|
||||
ImGui::TextWrapped("%s", z3ed_command_state_.command_output.c_str());
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
void AgentChatWidget::RenderRomSyncPanel() {
|
||||
if (!ImGui::CollapsingHeader(ICON_MD_SYNC " ROM Synchronization",
|
||||
ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.18f, 0.14f, 0.12f, 1.0f));
|
||||
ImGui::BeginChild("RomSync", ImVec2(0, 250), true);
|
||||
|
||||
ImGui::Text(ICON_MD_STORAGE " ROM State");
|
||||
ImGui::Separator();
|
||||
|
||||
// Display current ROM hash
|
||||
if (!rom_sync_state_.current_rom_hash.empty()) {
|
||||
ImGui::Text("Hash: %s", rom_sync_state_.current_rom_hash.substr(0, 16).c_str());
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton(ICON_MD_CONTENT_COPY)) {
|
||||
ImGui::SetClipboardText(rom_sync_state_.current_rom_hash.c_str());
|
||||
if (toast_manager_) {
|
||||
toast_manager_->Show("ROM hash copied", ToastType::kInfo, 2.0f);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ImGui::TextDisabled("No ROM loaded");
|
||||
}
|
||||
|
||||
if (rom_sync_state_.last_sync_time != absl::InfinitePast()) {
|
||||
ImGui::Text("Last Sync: %s",
|
||||
absl::FormatTime("%H:%M:%S", rom_sync_state_.last_sync_time,
|
||||
absl::LocalTimeZone()).c_str());
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Checkbox("Auto-sync ROM changes", &rom_sync_state_.auto_sync_enabled);
|
||||
|
||||
if (rom_sync_state_.auto_sync_enabled) {
|
||||
ImGui::SliderInt("Sync Interval (seconds)",
|
||||
&rom_sync_state_.sync_interval_seconds, 10, 120);
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
|
||||
bool can_sync = static_cast<bool>(rom_sync_callbacks_.generate_rom_diff) &&
|
||||
collaboration_state_.active &&
|
||||
collaboration_state_.mode == CollaborationMode::kNetwork;
|
||||
|
||||
if (!can_sync) ImGui::BeginDisabled();
|
||||
|
||||
if (ImGui::Button(ICON_MD_CLOUD_UPLOAD " Send ROM Sync", ImVec2(-1, 0))) {
|
||||
if (rom_sync_callbacks_.generate_rom_diff) {
|
||||
auto diff_result = rom_sync_callbacks_.generate_rom_diff();
|
||||
if (diff_result.ok()) {
|
||||
std::string hash = rom_sync_callbacks_.get_rom_hash
|
||||
? rom_sync_callbacks_.get_rom_hash()
|
||||
: "";
|
||||
|
||||
rom_sync_state_.current_rom_hash = hash;
|
||||
rom_sync_state_.last_sync_time = absl::Now();
|
||||
|
||||
// TODO: Send via network coordinator
|
||||
if (toast_manager_) {
|
||||
toast_manager_->Show(ICON_MD_CLOUD_DONE " ROM synced to collaborators",
|
||||
ToastType::kSuccess, 3.0f);
|
||||
}
|
||||
} else if (toast_manager_) {
|
||||
toast_manager_->Show(
|
||||
absl::StrFormat(ICON_MD_ERROR " Sync failed: %s",
|
||||
diff_result.status().message()),
|
||||
ToastType::kError, 5.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!can_sync) {
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Connect to a network session to sync ROM");
|
||||
}
|
||||
}
|
||||
|
||||
// Show pending syncs
|
||||
if (!rom_sync_state_.pending_syncs.empty()) {
|
||||
ImGui::Spacing();
|
||||
ImGui::Text(ICON_MD_PENDING " Pending Syncs (%zu)",
|
||||
rom_sync_state_.pending_syncs.size());
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::BeginChild("PendingSyncs", ImVec2(0, 80), true);
|
||||
for (const auto& sync : rom_sync_state_.pending_syncs) {
|
||||
ImGui::BulletText("%s", sync.substr(0, 40).c_str());
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
void AgentChatWidget::RenderSnapshotPreviewPanel() {
|
||||
if (!ImGui::CollapsingHeader(ICON_MD_PHOTO_CAMERA " Snapshot Preview",
|
||||
ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.12f, 0.12f, 0.16f, 1.0f));
|
||||
ImGui::BeginChild("SnapshotPreview", ImVec2(0, 300), true);
|
||||
|
||||
if (multimodal_state_.last_capture_path.has_value()) {
|
||||
ImGui::Text(ICON_MD_IMAGE " Latest Capture");
|
||||
ImGui::Separator();
|
||||
ImGui::TextWrapped("%s",
|
||||
multimodal_state_.last_capture_path->filename().string().c_str());
|
||||
|
||||
// TODO: Load and display image thumbnail
|
||||
ImGui::TextDisabled("Preview: [Image preview not yet implemented]");
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
bool can_share = collaboration_state_.active &&
|
||||
collaboration_state_.mode == CollaborationMode::kNetwork;
|
||||
|
||||
if (!can_share) ImGui::BeginDisabled();
|
||||
|
||||
if (ImGui::Button(ICON_MD_SHARE " Share with Collaborators", ImVec2(-1, 0))) {
|
||||
// TODO: Share snapshot via network coordinator
|
||||
if (toast_manager_) {
|
||||
toast_manager_->Show(ICON_MD_CHECK " Snapshot shared",
|
||||
ToastType::kSuccess, 3.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (!can_share) {
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Connect to a network session to share snapshots");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ImGui::TextDisabled(ICON_MD_NO_PHOTOGRAPHY " No snapshot captured yet");
|
||||
ImGui::TextWrapped("Use the Multimodal panel to capture a snapshot");
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
void AgentChatWidget::RenderProposalManagerPanel() {
|
||||
ImGui::Text(ICON_MD_PREVIEW " Proposal Management");
|
||||
ImGui::Separator();
|
||||
|
||||
if (z3ed_callbacks_.list_proposals) {
|
||||
auto proposals_result = z3ed_callbacks_.list_proposals();
|
||||
|
||||
if (proposals_result.ok()) {
|
||||
const auto& proposals = *proposals_result;
|
||||
|
||||
ImGui::Text("Total Proposals: %zu", proposals.size());
|
||||
ImGui::Spacing();
|
||||
|
||||
if (proposals.empty()) {
|
||||
ImGui::TextDisabled("No proposals yet. Use the agent to create proposals.");
|
||||
} else {
|
||||
if (ImGui::BeginTable("ProposalsTable", 3,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_Resizable)) {
|
||||
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 100.0f);
|
||||
ImGui::TableSetupColumn("Description", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, 150.0f);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (const auto& proposal_id : proposals) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(proposal_id.c_str());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextDisabled("Proposal details...");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::PushID(proposal_id.c_str());
|
||||
|
||||
if (ImGui::SmallButton(ICON_MD_VISIBILITY)) {
|
||||
FocusProposalDrawer(proposal_id);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::SmallButton(ICON_MD_CHECK)) {
|
||||
if (z3ed_callbacks_.accept_proposal) {
|
||||
auto status = z3ed_callbacks_.accept_proposal(proposal_id);
|
||||
(void)status; // Acknowledge result
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::SmallButton(ICON_MD_CLOSE)) {
|
||||
if (z3ed_callbacks_.reject_proposal) {
|
||||
auto status = z3ed_callbacks_.reject_proposal(proposal_id);
|
||||
(void)status; // Acknowledge result
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::string error_msg(proposals_result.status().message());
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f),
|
||||
"Failed to load proposals: %s",
|
||||
error_msg.c_str());
|
||||
}
|
||||
} else {
|
||||
ImGui::TextDisabled("Proposal management not available");
|
||||
ImGui::TextWrapped("Set up Z3ED command callbacks to enable this feature");
|
||||
}
|
||||
}
|
||||
|
||||
void AgentChatWidget::HandleRomSyncReceived(const std::string& diff_data,
|
||||
const std::string& rom_hash) {
|
||||
if (rom_sync_callbacks_.apply_rom_diff) {
|
||||
auto status = rom_sync_callbacks_.apply_rom_diff(diff_data, rom_hash);
|
||||
|
||||
if (status.ok()) {
|
||||
rom_sync_state_.current_rom_hash = rom_hash;
|
||||
rom_sync_state_.last_sync_time = absl::Now();
|
||||
|
||||
if (toast_manager_) {
|
||||
toast_manager_->Show(ICON_MD_CLOUD_DOWNLOAD " ROM sync applied from collaborator",
|
||||
ToastType::kInfo, 3.5f);
|
||||
}
|
||||
} else if (toast_manager_) {
|
||||
toast_manager_->Show(
|
||||
absl::StrFormat(ICON_MD_ERROR " ROM sync failed: %s", status.message()),
|
||||
ToastType::kError, 5.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AgentChatWidget::HandleSnapshotReceived(
|
||||
[[maybe_unused]] const std::string& snapshot_data,
|
||||
const std::string& snapshot_type) {
|
||||
// TODO: Decode and store snapshot for preview
|
||||
if (toast_manager_) {
|
||||
toast_manager_->Show(
|
||||
absl::StrFormat(ICON_MD_PHOTO " Snapshot received: %s", snapshot_type),
|
||||
ToastType::kInfo, 3.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void AgentChatWidget::HandleProposalReceived(
|
||||
[[maybe_unused]] const std::string& proposal_data) {
|
||||
// TODO: Parse and add proposal to local registry
|
||||
if (toast_manager_) {
|
||||
toast_manager_->Show(ICON_MD_LIGHTBULB " New proposal received from collaborator",
|
||||
ToastType::kInfo, 3.5f);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
@@ -21,6 +21,20 @@ namespace editor {
|
||||
class ProposalDrawer;
|
||||
class ToastManager;
|
||||
|
||||
/**
|
||||
* @class AgentChatWidget
|
||||
* @brief Modern AI chat widget with comprehensive z3ed and yaze-server integration
|
||||
*
|
||||
* Features:
|
||||
* - AI Provider Configuration (Ollama, Gemini, Mock)
|
||||
* - Z3ED Command Palette (run, plan, diff, accept, test)
|
||||
* - Real-time Collaboration (Local & Network modes)
|
||||
* - ROM Synchronization and Diff Broadcasting
|
||||
* - Multimodal Vision (Screenshot Analysis)
|
||||
* - Snapshot Sharing with Preview
|
||||
* - Collaborative Proposal Management
|
||||
* - Tabbed Interface with Modern ImGui patterns
|
||||
*/
|
||||
class AgentChatWidget {
|
||||
public:
|
||||
AgentChatWidget();
|
||||
@@ -47,6 +61,23 @@ class AgentChatWidget {
|
||||
std::function<absl::Status(const std::filesystem::path&, const std::string&)> send_to_gemini;
|
||||
};
|
||||
|
||||
// Z3ED Command Callbacks
|
||||
struct Z3EDCommandCallbacks {
|
||||
std::function<absl::Status(const std::string&)> run_agent_task;
|
||||
std::function<absl::StatusOr<std::string>(const std::string&)> plan_agent_task;
|
||||
std::function<absl::StatusOr<std::string>(const std::string&)> diff_proposal;
|
||||
std::function<absl::Status(const std::string&)> accept_proposal;
|
||||
std::function<absl::Status(const std::string&)> reject_proposal;
|
||||
std::function<absl::StatusOr<std::vector<std::string>>()> list_proposals;
|
||||
};
|
||||
|
||||
// ROM Sync Callbacks
|
||||
struct RomSyncCallbacks {
|
||||
std::function<absl::StatusOr<std::string>()> generate_rom_diff;
|
||||
std::function<absl::Status(const std::string&, const std::string&)> apply_rom_diff;
|
||||
std::function<std::string()> get_rom_hash;
|
||||
};
|
||||
|
||||
void SetToastManager(ToastManager* toast_manager);
|
||||
|
||||
void SetProposalDrawer(ProposalDrawer* drawer);
|
||||
@@ -59,6 +90,14 @@ class AgentChatWidget {
|
||||
multimodal_callbacks_ = callbacks;
|
||||
}
|
||||
|
||||
void SetZ3EDCommandCallbacks(const Z3EDCommandCallbacks& callbacks) {
|
||||
z3ed_callbacks_ = callbacks;
|
||||
}
|
||||
|
||||
void SetRomSyncCallbacks(const RomSyncCallbacks& callbacks) {
|
||||
rom_sync_callbacks_ = callbacks;
|
||||
}
|
||||
|
||||
bool* active() { return &active_; }
|
||||
bool is_active() const { return active_; }
|
||||
void set_active(bool active) { active_ = active; }
|
||||
@@ -94,12 +133,49 @@ public:
|
||||
char specific_window_buffer[128] = {};
|
||||
};
|
||||
|
||||
// Agent Configuration State
|
||||
struct AgentConfigState {
|
||||
std::string ai_provider = "mock"; // mock, ollama, gemini
|
||||
std::string ai_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;
|
||||
int max_retry_attempts = 3;
|
||||
char provider_buffer[32] = "mock";
|
||||
char model_buffer[128] = {};
|
||||
char ollama_host_buffer[256] = "http://localhost:11434";
|
||||
char gemini_key_buffer[256] = {};
|
||||
};
|
||||
|
||||
// ROM Sync State
|
||||
struct RomSyncState {
|
||||
std::string current_rom_hash;
|
||||
absl::Time last_sync_time = absl::InfinitePast();
|
||||
bool auto_sync_enabled = false;
|
||||
int sync_interval_seconds = 30;
|
||||
std::vector<std::string> pending_syncs;
|
||||
};
|
||||
|
||||
// Z3ED Command State
|
||||
struct Z3EDCommandState {
|
||||
std::string last_command;
|
||||
std::string command_output;
|
||||
bool command_running = false;
|
||||
char command_input_buffer[512] = {};
|
||||
};
|
||||
|
||||
// Accessors for capture settings
|
||||
CaptureMode capture_mode() const { return multimodal_state_.capture_mode; }
|
||||
const char* specific_window_name() const {
|
||||
return multimodal_state_.specific_window_buffer;
|
||||
}
|
||||
|
||||
// Agent configuration accessors
|
||||
const AgentConfigState& GetAgentConfig() const { return agent_config_; }
|
||||
void UpdateAgentConfig(const AgentConfigState& config);
|
||||
|
||||
// Collaboration history management (public so EditorManager can call them)
|
||||
void SwitchToSharedHistory(const std::string& session_id);
|
||||
void SwitchToLocalHistory();
|
||||
@@ -120,12 +196,20 @@ public:
|
||||
int new_total_proposals);
|
||||
void RenderCollaborationPanel();
|
||||
void RenderMultimodalPanel();
|
||||
void RenderAgentConfigPanel();
|
||||
void RenderZ3EDCommandPanel();
|
||||
void RenderRomSyncPanel();
|
||||
void RenderSnapshotPreviewPanel();
|
||||
void RenderProposalManagerPanel();
|
||||
void RefreshCollaboration();
|
||||
void ApplyCollaborationSession(
|
||||
const CollaborationCallbacks::SessionContext& context,
|
||||
bool update_action_timestamp);
|
||||
void MarkHistoryDirty();
|
||||
void PollSharedHistory(); // For real-time collaboration sync
|
||||
void HandleRomSyncReceived(const std::string& diff_data, const std::string& rom_hash);
|
||||
void HandleSnapshotReceived(const std::string& snapshot_data, const std::string& snapshot_type);
|
||||
void HandleProposalReceived(const std::string& proposal_data);
|
||||
|
||||
cli::agent::ConversationalAgentService agent_service_;
|
||||
char input_buffer_[1024];
|
||||
@@ -142,17 +226,38 @@ public:
|
||||
ProposalDrawer* proposal_drawer_ = nullptr;
|
||||
std::string pending_focus_proposal_id_;
|
||||
absl::Time last_persist_time_ = absl::InfinitePast();
|
||||
|
||||
// Main state
|
||||
CollaborationState collaboration_state_;
|
||||
CollaborationCallbacks collaboration_callbacks_;
|
||||
MultimodalState multimodal_state_;
|
||||
AgentConfigState agent_config_;
|
||||
RomSyncState rom_sync_state_;
|
||||
Z3EDCommandState z3ed_command_state_;
|
||||
|
||||
// Callbacks
|
||||
CollaborationCallbacks collaboration_callbacks_;
|
||||
MultimodalCallbacks multimodal_callbacks_;
|
||||
Z3EDCommandCallbacks z3ed_callbacks_;
|
||||
RomSyncCallbacks rom_sync_callbacks_;
|
||||
|
||||
// Input buffers
|
||||
char session_name_buffer_[64] = {};
|
||||
char join_code_buffer_[64] = {};
|
||||
char server_url_buffer_[256] = "ws://localhost:8765";
|
||||
char multimodal_prompt_buffer_[256] = {};
|
||||
|
||||
// Timing
|
||||
absl::Time last_collaboration_action_ = absl::InfinitePast();
|
||||
absl::Time last_shared_history_poll_ = absl::InfinitePast();
|
||||
size_t last_known_history_size_ = 0;
|
||||
|
||||
// UI state
|
||||
int active_tab_ = 0; // 0=Chat, 1=Config, 2=Commands, 3=Collab, 4=ROM Sync
|
||||
bool show_agent_config_ = false;
|
||||
bool show_z3ed_commands_ = false;
|
||||
bool show_rom_sync_ = false;
|
||||
bool show_snapshot_preview_ = false;
|
||||
std::vector<uint8_t> snapshot_preview_data_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
|
||||
Reference in New Issue
Block a user