feat: Enhance AgentChatWidget with Chat Session Management and UI Improvements

- Implemented functionality to save, load, and delete chat sessions, allowing users to manage their chat history effectively.
- Introduced a new layout for the connection status bar and improved the AI provider selection interface for better visibility.
- Enhanced the UI of the AgentEditor with a modular 3-column layout, including dedicated tabs for system prompts and common tiles, improving user experience and organization.
This commit is contained in:
scawful
2025-10-05 12:29:32 -04:00
parent 744c49ffc8
commit 44df204332
5 changed files with 375 additions and 79 deletions

View File

@@ -799,11 +799,10 @@ void AgentChatWidget::Draw() {
? ImVec4(0.133f, 0.545f, 0.133f, 1.0f)
: ImVec4(0.6f, 0.6f, 0.6f, 1.0f);
// Connection status bar at top (compact)
// Connection status bar at top (taller for better visibility)
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 bar_start = ImGui::GetCursorScreenPos();
ImVec2 bar_size(ImGui::GetContentRegionAvail().x,
55); // Reduced from 75 to 55
ImVec2 bar_size(ImGui::GetContentRegionAvail().x, 90); // Increased from 55
// Gradient background
ImU32 color_top = ImGui::GetColorU32(ImVec4(0.18f, 0.22f, 0.28f, 1.0f));
@@ -831,15 +830,14 @@ void AgentChatWidget::Draw() {
float vertical_padding = (55.0f - content_height) / 2.0f;
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + vertical_padding);
// Compact single row layout
ImGui::TextColored(accent_color, ICON_MD_SMART_TOY);
ImGui::SameLine();
ImGui::SetNextItemWidth(95);
const char* providers[] = {"Mock", "Ollama", "Gemini"};
int current_provider = (agent_config_.ai_provider == "mock") ? 0
: (agent_config_.ai_provider == "ollama") ? 1
: 2;
if (ImGui::Combo("##main_provider", &current_provider, providers, 3)) {
// Two-row layout for better visibility
ImGui::Text(ICON_MD_SMART_TOY " AI Provider:");
ImGui::SetNextItemWidth(-1);
const char* providers[] = {"Mock", "Ollama", "Gemini"};
int current_provider = (agent_config_.ai_provider == "mock") ? 0
: (agent_config_.ai_provider == "ollama") ? 1
: 2;
if (ImGui::Combo("##main_provider", &current_provider, providers, 3)) {
agent_config_.ai_provider = (current_provider == 0) ? "mock"
: (current_provider == 1) ? "ollama"
: "gemini";
@@ -2075,6 +2073,99 @@ void AgentChatWidget::SyncHistoryToPopup() {
chat_history_popup_->NotifyNewMessage();
}
std::filesystem::path AgentChatWidget::GetSessionsDirectory() {
std::filesystem::path config_dir(yaze::util::GetConfigDirectory());
if (config_dir.empty()) {
#ifdef _WIN32
const char* appdata = std::getenv("APPDATA");
if (appdata) {
config_dir = std::filesystem::path(appdata) / "yaze";
}
#else
const char* home = std::getenv("HOME");
if (home) {
config_dir = std::filesystem::path(home) / ".yaze";
}
#endif
}
return config_dir / "chats";
}
void AgentChatWidget::SaveChatSession(const ChatSession& session) {
auto sessions_dir = GetSessionsDirectory();
std::error_code ec;
std::filesystem::create_directories(sessions_dir, ec);
std::filesystem::path save_path = sessions_dir / (session.id + ".json");
// Save using existing history codec
AgentChatHistoryCodec::Snapshot snapshot;
snapshot.history = session.agent_service.GetHistory();
auto status = AgentChatHistoryCodec::Save(save_path, snapshot);
if (status.ok() && toast_manager_) {
toast_manager_->Show(
absl::StrFormat(ICON_MD_SAVE " Chat '%s' saved", session.name),
ToastType::kSuccess, 2.0f);
}
}
void AgentChatWidget::LoadChatSession(const std::string& session_id) {
auto sessions_dir = GetSessionsDirectory();
std::filesystem::path load_path = sessions_dir / (session_id + ".json");
if (!std::filesystem::exists(load_path)) {
if (toast_manager_) {
toast_manager_->Show(ICON_MD_WARNING " Session file not found", ToastType::kWarning, 2.5f);
}
return;
}
auto snapshot_result = AgentChatHistoryCodec::Load(load_path);
if (snapshot_result.ok()) {
// Create new session with loaded history
ChatSession session(session_id, session_id);
session.agent_service.ReplaceHistory(snapshot_result->history);
session.history_loaded = true;
chat_sessions_.push_back(std::move(session));
active_session_index_ = static_cast<int>(chat_sessions_.size() - 1);
if (toast_manager_) {
toast_manager_->Show(ICON_MD_CHECK_CIRCLE " Chat session loaded", ToastType::kSuccess, 2.0f);
}
}
}
void AgentChatWidget::DeleteChatSession(const std::string& session_id) {
auto sessions_dir = GetSessionsDirectory();
std::filesystem::path session_path = sessions_dir / (session_id + ".json");
if (std::filesystem::exists(session_path)) {
std::filesystem::remove(session_path);
if (toast_manager_) {
toast_manager_->Show(ICON_MD_DELETE " Chat deleted", ToastType::kInfo, 2.0f);
}
}
}
std::vector<std::string> AgentChatWidget::GetSavedSessions() {
std::vector<std::string> sessions;
auto sessions_dir = GetSessionsDirectory();
if (!std::filesystem::exists(sessions_dir)) {
return sessions;
}
for (const auto& entry : std::filesystem::directory_iterator(sessions_dir)) {
if (entry.path().extension() == ".json") {
sessions.push_back(entry.path().stem().string());
}
}
return sessions;
}
void AgentChatWidget::RenderSystemPromptEditor() {
ImGui::BeginChild("SystemPromptEditor", ImVec2(0, 0), false);

View File

@@ -244,17 +244,25 @@ public:
struct ChatSession {
std::string id;
std::string name;
std::filesystem::path save_path;
cli::agent::ConversationalAgentService agent_service;
size_t last_history_size = 0;
bool history_loaded = false;
bool history_dirty = false;
std::filesystem::path history_path;
absl::Time created_at = absl::Now();
absl::Time last_persist_time = absl::InfinitePast();
ChatSession(const std::string& session_id, const std::string& session_name)
: id(session_id), name(session_name) {}
};
void SaveChatSession(const ChatSession& session);
void LoadChatSession(const std::string& session_id);
void DeleteChatSession(const std::string& session_id);
std::vector<std::string> GetSavedSessions();
std::filesystem::path GetSessionsDirectory();
std::vector<ChatSession> chat_sessions_;
int active_session_index_ = 0;

View File

@@ -5,6 +5,7 @@
#include <memory>
#include "absl/strings/str_format.h"
#include "absl/strings/match.h"
#include "absl/time/clock.h"
#include "app/editor/agent/agent_chat_widget.h"
#include "app/editor/agent/agent_collaboration_coordinator.h"
@@ -31,6 +32,7 @@ AgentEditor::AgentEditor() {
chat_widget_ = std::make_unique<AgentChatWidget>();
local_coordinator_ = std::make_unique<AgentCollaborationCoordinator>();
prompt_editor_ = std::make_unique<TextEditor>();
common_tiles_editor_ = std::make_unique<TextEditor>();
// Initialize default configuration (legacy)
current_config_.provider = "mock";
@@ -46,11 +48,15 @@ AgentEditor::AgentEditor() {
current_profile_.max_retry_attempts = 3;
current_profile_.tags = {"default", "z3ed"};
// Setup text editor for system prompts
// Setup text editors
prompt_editor_->SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus());
prompt_editor_->SetReadOnly(false);
prompt_editor_->SetShowWhitespaces(false);
common_tiles_editor_->SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus());
common_tiles_editor_->SetReadOnly(false);
common_tiles_editor_->SetShowWhitespaces(false);
// Ensure profiles directory exists
EnsureProfilesDirectory();
}
@@ -173,34 +179,58 @@ void AgentEditor::DrawDashboard() {
// Compact tabbed interface (combined tabs)
if (ImGui::BeginTabBar("AgentEditorTabs", ImGuiTabBarFlags_None)) {
// Bot Management Tab (combines Config + Profiles + Prompts)
// Bot Studio Tab - Modular 3-column layout
if (ImGui::BeginTabItem(ICON_MD_SMART_TOY " Bot Studio")) {
ImGui::Spacing();
// Three-column layout with prompt editor in center
if (ImGui::BeginTable("BotStudioLayout", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) {
// Use ImGui table for clean 3-column resizable layout
ImGuiTableFlags table_flags = ImGuiTableFlags_Resizable |
ImGuiTableFlags_BordersInnerV |
ImGuiTableFlags_SizingStretchProp;
if (ImGui::BeginTable("BotStudioLayout", 3, table_flags)) {
ImGui::TableSetupColumn("Config", ImGuiTableColumnFlags_WidthFixed, 380.0f);
ImGui::TableSetupColumn("Prompt Editor", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("Editors", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("Profiles", ImGuiTableColumnFlags_WidthFixed, 320.0f);
ImGui::TableNextRow();
// LEFT: Configuration
ImGui::TableSetColumnIndex(0);
ImGui::BeginChild("ConfigSection", ImVec2(0, 0), false);
// Column 1: Configuration
ImGui::TableNextColumn();
ImGui::PushID("ConfigColumn");
DrawConfigurationPanel();
ImGui::EndChild();
ImGui::PopID();
// CENTER: System Prompt Editor
ImGui::TableSetColumnIndex(1);
ImGui::BeginChild("PromptSection", ImVec2(0, 0), false);
DrawPromptEditorPanel();
ImGui::EndChild();
// Column 2: Editors (Prompt + Tiles + New)
ImGui::TableNextColumn();
ImGui::PushID("EditorsColumn");
// RIGHT: Bot Profiles List
ImGui::TableSetColumnIndex(2);
ImGui::BeginChild("ProfilesSection", ImVec2(0, 0), false);
// Tabbed editors for better organization
if (ImGui::BeginTabBar("EditorTabs", ImGuiTabBarFlags_None)) {
if (ImGui::BeginTabItem(ICON_MD_EDIT " System Prompt")) {
DrawPromptEditorPanel();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem(ICON_MD_GRID_ON " Common Tiles")) {
DrawCommonTilesEditor();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem(ICON_MD_ADD " New Prompt")) {
DrawNewPromptCreator();
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
ImGui::PopID();
// Column 3: Bot Profiles
ImGui::TableNextColumn();
ImGui::PushID("ProfilesColumn");
DrawBotProfilesPanel();
ImGui::EndChild();
ImGui::PopID();
ImGui::EndTable();
}
@@ -771,6 +801,185 @@ void AgentEditor::DrawAdvancedMetricsPanel() {
}
}
void AgentEditor::DrawCommonTilesEditor() {
ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_GRID_ON " Common Tiles Reference");
ImGui::Separator();
ImGui::Spacing();
ImGui::TextWrapped("Customize the tile reference file that AI uses for tile placement. "
"Organize tiles by category and provide hex IDs with descriptions.");
ImGui::Spacing();
// Load/Save buttons
if (ImGui::Button(ICON_MD_FOLDER_OPEN " Load", ImVec2(100, 0))) {
auto content = core::AssetLoader::LoadTextFile("agent/common_tiles.txt");
if (content.ok()) {
common_tiles_editor_->SetText(*content);
common_tiles_initialized_ = true;
if (toast_manager_) {
toast_manager_->Show(ICON_MD_CHECK_CIRCLE " Common tiles loaded", ToastType::kSuccess, 2.0f);
}
}
}
ImGui::SameLine();
if (ImGui::Button(ICON_MD_SAVE " Save", ImVec2(100, 0))) {
// Save to project or assets directory
if (toast_manager_) {
toast_manager_->Show(ICON_MD_INFO " Save to project directory (coming soon)", ToastType::kInfo, 2.0f);
}
}
ImGui::SameLine();
if (ImGui::SmallButton(ICON_MD_REFRESH)) {
common_tiles_initialized_ = false;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Reload from disk");
}
// Load if not initialized
if (!common_tiles_initialized_ && common_tiles_editor_) {
auto content = core::AssetLoader::LoadTextFile("agent/common_tiles.txt");
if (content.ok()) {
common_tiles_editor_->SetText(*content);
} else {
// Create default template
std::string default_tiles =
"# Common Tile16 Reference\n"
"# Format: 0xHEX = Description\n\n"
"[grass_tiles]\n"
"0x020 = Grass (standard)\n\n"
"[nature_tiles]\n"
"0x02E = Tree (oak)\n"
"0x003 = Bush\n\n"
"[water_tiles]\n"
"0x14C = Water (top edge)\n"
"0x14D = Water (middle)\n";
common_tiles_editor_->SetText(default_tiles);
}
common_tiles_initialized_ = true;
}
ImGui::Separator();
ImGui::Spacing();
// Editor
if (common_tiles_editor_) {
ImVec2 editor_size(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y);
common_tiles_editor_->Render("##tiles_editor", editor_size, true);
}
}
void AgentEditor::DrawNewPromptCreator() {
ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_ADD " Create New System Prompt");
ImGui::Separator();
ImGui::Spacing();
ImGui::TextWrapped("Create a custom system prompt from scratch or use a template.");
ImGui::Spacing();
ImGui::Separator();
// Prompt name input
ImGui::Text("Prompt Name:");
ImGui::SetNextItemWidth(-1);
ImGui::InputTextWithHint("##new_prompt_name", "e.g., custom_prompt.txt",
new_prompt_name_, sizeof(new_prompt_name_));
ImGui::Spacing();
// Template selection
ImGui::Text("Start from template:");
if (ImGui::Button(ICON_MD_FILE_COPY " v1 (Basic)", ImVec2(-1, 0))) {
auto content = core::AssetLoader::LoadTextFile("agent/system_prompt.txt");
if (content.ok() && prompt_editor_) {
prompt_editor_->SetText(*content);
if (toast_manager_) {
toast_manager_->Show("Template v1 loaded", ToastType::kSuccess, 1.5f);
}
}
}
if (ImGui::Button(ICON_MD_FILE_COPY " v2 (Enhanced)", ImVec2(-1, 0))) {
auto content = core::AssetLoader::LoadTextFile("agent/system_prompt_v2.txt");
if (content.ok() && prompt_editor_) {
prompt_editor_->SetText(*content);
if (toast_manager_) {
toast_manager_->Show("Template v2 loaded", ToastType::kSuccess, 1.5f);
}
}
}
if (ImGui::Button(ICON_MD_FILE_COPY " v3 (Proactive)", ImVec2(-1, 0))) {
auto content = core::AssetLoader::LoadTextFile("agent/system_prompt_v3.txt");
if (content.ok() && prompt_editor_) {
prompt_editor_->SetText(*content);
if (toast_manager_) {
toast_manager_->Show("Template v3 loaded", ToastType::kSuccess, 1.5f);
}
}
}
if (ImGui::Button(ICON_MD_NOTE_ADD " Blank Template", ImVec2(-1, 0))) {
if (prompt_editor_) {
std::string blank_template =
"# Custom System Prompt\n\n"
"You are an AI assistant for ROM hacking.\n\n"
"## Your Role\n"
"- Help users understand ROM data\n"
"- Provide accurate information\n"
"- Use tools when needed\n\n"
"## Available Tools\n"
"- resource-list: List resources by type\n"
"- dungeon-describe-room: Get room details\n"
"- overworld-find-tile: Find tile locations\n"
"- ... (see function schemas for complete list)\n\n"
"## Guidelines\n"
"1. Always provide text_response after tool calls\n"
"2. Be helpful and accurate\n"
"3. Explain your reasoning\n";
prompt_editor_->SetText(blank_template);
if (toast_manager_) {
toast_manager_->Show("Blank template created", ToastType::kSuccess, 1.5f);
}
}
}
ImGui::Spacing();
ImGui::Separator();
// Save button
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.133f, 0.545f, 0.133f, 0.8f));
if (ImGui::Button(ICON_MD_SAVE " Save New Prompt", ImVec2(-1, 40))) {
if (std::strlen(new_prompt_name_) > 0 && prompt_editor_) {
// Save to assets/agent/ directory
std::string filename = new_prompt_name_;
if (!absl::EndsWith(filename, ".txt")) {
filename += ".txt";
}
// TODO: Actually save the file
if (toast_manager_) {
toast_manager_->Show(
absl::StrFormat(ICON_MD_SAVE " Prompt saved as %s", filename),
ToastType::kSuccess, 3.0f);
}
// Clear name buffer
std::memset(new_prompt_name_, 0, sizeof(new_prompt_name_));
} else if (toast_manager_) {
toast_manager_->Show(ICON_MD_WARNING " Enter a name for the prompt", ToastType::kWarning, 2.0f);
}
}
ImGui::PopStyleColor();
ImGui::Spacing();
ImGui::TextWrapped("Note: New prompts are saved to your project. Use 'System Prompt' tab to edit existing prompts.");
}
// Bot Profile Management Implementation
absl::Status AgentEditor::SaveBotProfile(const BotProfile& profile) {
#if defined(YAZE_WITH_JSON)

View File

@@ -182,6 +182,8 @@ class AgentEditor : public Editor {
void DrawBotProfilesPanel();
void DrawChatHistoryViewer();
void DrawAdvancedMetricsPanel();
void DrawCommonTilesEditor();
void DrawNewPromptCreator();
// Setup callbacks
void SetupChatWidgetCallbacks();
@@ -213,8 +215,11 @@ class AgentEditor : public Editor {
// System Prompt Editor
std::unique_ptr<TextEditor> prompt_editor_;
std::unique_ptr<TextEditor> common_tiles_editor_;
bool prompt_editor_initialized_ = false;
bool common_tiles_initialized_ = false;
std::string active_prompt_file_ = "system_prompt_v3.txt";
char new_prompt_name_[128] = {};
// Collaboration state
CollaborationMode current_mode_ = CollaborationMode::kLocal;

View File

@@ -30,33 +30,29 @@ AgentChatHistoryPopup::AgentChatHistoryPopup() {
void AgentChatHistoryPopup::Draw() {
if (!visible_) return;
// Set drawer position on the LEFT side with beautiful styling
// Set drawer position on the LEFT side (full height)
ImGuiIO& io = ImGui::GetIO();
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(drawer_width_, io.DisplaySize.y),
ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(drawer_width_, io.DisplaySize.y), ImGuiCond_Always);
ImGuiWindowFlags flags = ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoTitleBar;
// Theme-matched styling
// Use current theme colors
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10, 10));
if (ImGui::Begin("##AgentChatPopup", &visible_, flags)) {
// Animated header pulse
header_pulse_ += io.DeltaTime * 2.0f;
float pulse = 0.5f + 0.5f * sinf(header_pulse_);
DrawHeader();
ImGui::Separator();
ImGui::Spacing();
// Message list with gradient background
float list_height = io.DisplaySize.y - 280.0f; // Reserve space for input and actions
// Calculate proper list height
float list_height = ImGui::GetContentRegionAvail().y - 220.0f;
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.05f, 0.05f, 0.08f, 0.95f));
ImGui::BeginChild("MessageList", ImVec2(0, list_height), true, ImGuiWindowFlags_AlwaysVerticalScrollbar);
@@ -116,18 +112,20 @@ void AgentChatHistoryPopup::DrawMessage(const cli::agent::ChatMessage& msg, int
ImGui::PushID(index);
bool from_user = (msg.sender == cli::agent::ChatMessage::Sender::kUser);
ImVec4 header_color = from_user ? kUserColor : kAgentColor;
// Use theme colors with slight tint
ImVec4 text_color = ImGui::GetStyleColorVec4(ImGuiCol_Text);
ImVec4 header_color = from_user
? ImVec4(text_color.x * 1.2f, text_color.y * 0.9f, text_color.z * 0.5f, 1.0f) // Gold tint
: ImVec4(text_color.x * 0.7f, text_color.y * 1.0f, text_color.z * 0.9f, 1.0f); // Teal tint
const char* sender_label = from_user ? ICON_MD_PERSON " You" : ICON_MD_SMART_TOY " Agent";
// Message header with sender and timestamp
ImGui::PushStyleColor(ImGuiCol_Text, header_color);
ImGui::Text("%s", sender_label);
ImGui::PopStyleColor();
// Message header
ImGui::TextColored(header_color, "%s", sender_label);
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Text, kTimestampColor);
ImGui::Text("%s", absl::FormatTime("%H:%M:%S", msg.timestamp, absl::LocalTimeZone()).c_str());
ImGui::PopStyleColor();
ImGui::TextDisabled("%s", absl::FormatTime("%H:%M:%S", msg.timestamp, absl::LocalTimeZone()).c_str());
// Message content
ImGui::Indent(10.0f);
@@ -181,10 +179,10 @@ void AgentChatHistoryPopup::DrawHeader() {
ImGui::Dummy(ImVec2(0, 8));
// Title with theme colors
ImGui::TextColored(ImGui::GetStyleColorVec4(ImGuiCol_Text), "%s AI Chat", ICON_MD_CHAT);
// Title
ImGui::Text("%s AI Chat", ICON_MD_CHAT);
ImGui::SameLine(ImGui::GetContentRegionAvail().x - 130);
ImGui::SameLine(ImGui::GetContentRegionAvail().x - 95);
// Compact mode toggle
if (ImGui::SmallButton(compact_mode_ ? ICON_MD_UNFOLD_MORE : ICON_MD_UNFOLD_LESS)) {
@@ -197,18 +195,25 @@ void AgentChatHistoryPopup::DrawHeader() {
ImGui::SameLine();
// Full chat button
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(kAccentColor.x, kAccentColor.y, kAccentColor.z, 0.6f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, kAccentColor);
if (ImGui::SmallButton(ICON_MD_OPEN_IN_NEW)) {
if (open_chat_callback_) {
open_chat_callback_();
}
}
ImGui::PopStyleColor(2);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Open full chat window");
}
ImGui::SameLine();
// Close button
if (ImGui::SmallButton(ICON_MD_CLOSE)) {
visible_ = false;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Close (Ctrl+H)");
}
// Message count with badge
int visible_count = 0;
for (const auto& msg : messages_) {
@@ -228,9 +233,6 @@ void AgentChatHistoryPopup::DrawHeader() {
}
void AgentChatHistoryPopup::DrawQuickActions() {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.15f, 0.2f, 0.8f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.2f, 0.2f, 0.3f, 0.9f));
float button_width = (ImGui::GetContentRegionAvail().x - 8) / 3.0f;
// Multimodal snapshot button
@@ -277,20 +279,13 @@ void AgentChatHistoryPopup::DrawQuickActions() {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Clear popup view");
}
ImGui::PopStyleColor(2);
}
void AgentChatHistoryPopup::DrawInputSection() {
ImGui::Separator();
ImGui::Spacing();
// Input field with beautiful styling
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.12f, 0.14f, 0.18f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.15f, 0.17f, 0.22f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.18f, 0.20f, 0.25f, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4.0f);
// Input field using theme colors
bool send_message = false;
ImGui::SetNextItemWidth(-1);
if (ImGui::InputTextMultiline("##popup_input", input_buffer_, sizeof(input_buffer_),
@@ -305,30 +300,18 @@ void AgentChatHistoryPopup::DrawInputSection() {
focus_input_ = false;
}
ImGui::PopStyleVar();
ImGui::PopStyleColor(3);
// Send button with gradient
// Send button
ImGui::Spacing();
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(kAccentColor.x, kAccentColor.y, kAccentColor.z, 0.7f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, kAccentColor);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.15f, 0.5f, 0.7f, 1.0f));
if (ImGui::Button(absl::StrFormat("%s Send", ICON_MD_SEND).c_str(), ImVec2(-1, 32)) || send_message) {
if (ImGui::Button(absl::StrFormat("%s Send", ICON_MD_SEND).c_str(), ImVec2(-1, 30)) || send_message) {
if (std::strlen(input_buffer_) > 0) {
SendMessage(input_buffer_);
std::memset(input_buffer_, 0, sizeof(input_buffer_));
}
}
ImGui::PopStyleColor(3);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Send message (Enter) • Ctrl+Enter for newline");
}
ImGui::Spacing();
ImGui::TextDisabled(ICON_MD_INFO " Enter: send • Ctrl+Enter: newline");
}
void AgentChatHistoryPopup::SendMessage(const std::string& message) {