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:
scawful
2025-10-04 20:12:02 -04:00
parent 712ed8dbf4
commit 1831ddfc49
2 changed files with 732 additions and 20 deletions

View File

@@ -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

View File

@@ -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