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:
@@ -799,11 +799,10 @@ void AgentChatWidget::Draw() {
|
|||||||
? ImVec4(0.133f, 0.545f, 0.133f, 1.0f)
|
? ImVec4(0.133f, 0.545f, 0.133f, 1.0f)
|
||||||
: ImVec4(0.6f, 0.6f, 0.6f, 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();
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||||
ImVec2 bar_start = ImGui::GetCursorScreenPos();
|
ImVec2 bar_start = ImGui::GetCursorScreenPos();
|
||||||
ImVec2 bar_size(ImGui::GetContentRegionAvail().x,
|
ImVec2 bar_size(ImGui::GetContentRegionAvail().x, 90); // Increased from 55
|
||||||
55); // Reduced from 75 to 55
|
|
||||||
|
|
||||||
// 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));
|
||||||
@@ -831,15 +830,14 @@ void AgentChatWidget::Draw() {
|
|||||||
float vertical_padding = (55.0f - content_height) / 2.0f;
|
float vertical_padding = (55.0f - content_height) / 2.0f;
|
||||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + vertical_padding);
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + vertical_padding);
|
||||||
|
|
||||||
// Compact single row layout
|
// Two-row layout for better visibility
|
||||||
ImGui::TextColored(accent_color, ICON_MD_SMART_TOY);
|
ImGui::Text(ICON_MD_SMART_TOY " AI Provider:");
|
||||||
ImGui::SameLine();
|
ImGui::SetNextItemWidth(-1);
|
||||||
ImGui::SetNextItemWidth(95);
|
const char* providers[] = {"Mock", "Ollama", "Gemini"};
|
||||||
const char* providers[] = {"Mock", "Ollama", "Gemini"};
|
int current_provider = (agent_config_.ai_provider == "mock") ? 0
|
||||||
int current_provider = (agent_config_.ai_provider == "mock") ? 0
|
: (agent_config_.ai_provider == "ollama") ? 1
|
||||||
: (agent_config_.ai_provider == "ollama") ? 1
|
: 2;
|
||||||
: 2;
|
if (ImGui::Combo("##main_provider", ¤t_provider, providers, 3)) {
|
||||||
if (ImGui::Combo("##main_provider", ¤t_provider, providers, 3)) {
|
|
||||||
agent_config_.ai_provider = (current_provider == 0) ? "mock"
|
agent_config_.ai_provider = (current_provider == 0) ? "mock"
|
||||||
: (current_provider == 1) ? "ollama"
|
: (current_provider == 1) ? "ollama"
|
||||||
: "gemini";
|
: "gemini";
|
||||||
@@ -2075,6 +2073,99 @@ void AgentChatWidget::SyncHistoryToPopup() {
|
|||||||
chat_history_popup_->NotifyNewMessage();
|
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() {
|
void AgentChatWidget::RenderSystemPromptEditor() {
|
||||||
ImGui::BeginChild("SystemPromptEditor", ImVec2(0, 0), false);
|
ImGui::BeginChild("SystemPromptEditor", ImVec2(0, 0), false);
|
||||||
|
|
||||||
|
|||||||
@@ -244,17 +244,25 @@ public:
|
|||||||
struct ChatSession {
|
struct ChatSession {
|
||||||
std::string id;
|
std::string id;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
std::filesystem::path save_path;
|
||||||
cli::agent::ConversationalAgentService agent_service;
|
cli::agent::ConversationalAgentService agent_service;
|
||||||
size_t last_history_size = 0;
|
size_t last_history_size = 0;
|
||||||
bool history_loaded = false;
|
bool history_loaded = false;
|
||||||
bool history_dirty = false;
|
bool history_dirty = false;
|
||||||
std::filesystem::path history_path;
|
std::filesystem::path history_path;
|
||||||
|
absl::Time created_at = absl::Now();
|
||||||
absl::Time last_persist_time = absl::InfinitePast();
|
absl::Time last_persist_time = absl::InfinitePast();
|
||||||
|
|
||||||
ChatSession(const std::string& session_id, const std::string& session_name)
|
ChatSession(const std::string& session_id, const std::string& session_name)
|
||||||
: id(session_id), name(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_;
|
std::vector<ChatSession> chat_sessions_;
|
||||||
int active_session_index_ = 0;
|
int active_session_index_ = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "absl/strings/str_format.h"
|
#include "absl/strings/str_format.h"
|
||||||
|
#include "absl/strings/match.h"
|
||||||
#include "absl/time/clock.h"
|
#include "absl/time/clock.h"
|
||||||
#include "app/editor/agent/agent_chat_widget.h"
|
#include "app/editor/agent/agent_chat_widget.h"
|
||||||
#include "app/editor/agent/agent_collaboration_coordinator.h"
|
#include "app/editor/agent/agent_collaboration_coordinator.h"
|
||||||
@@ -31,6 +32,7 @@ AgentEditor::AgentEditor() {
|
|||||||
chat_widget_ = std::make_unique<AgentChatWidget>();
|
chat_widget_ = std::make_unique<AgentChatWidget>();
|
||||||
local_coordinator_ = std::make_unique<AgentCollaborationCoordinator>();
|
local_coordinator_ = std::make_unique<AgentCollaborationCoordinator>();
|
||||||
prompt_editor_ = std::make_unique<TextEditor>();
|
prompt_editor_ = std::make_unique<TextEditor>();
|
||||||
|
common_tiles_editor_ = std::make_unique<TextEditor>();
|
||||||
|
|
||||||
// Initialize default configuration (legacy)
|
// Initialize default configuration (legacy)
|
||||||
current_config_.provider = "mock";
|
current_config_.provider = "mock";
|
||||||
@@ -46,11 +48,15 @@ AgentEditor::AgentEditor() {
|
|||||||
current_profile_.max_retry_attempts = 3;
|
current_profile_.max_retry_attempts = 3;
|
||||||
current_profile_.tags = {"default", "z3ed"};
|
current_profile_.tags = {"default", "z3ed"};
|
||||||
|
|
||||||
// Setup text editor for system prompts
|
// Setup text editors
|
||||||
prompt_editor_->SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus());
|
prompt_editor_->SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus());
|
||||||
prompt_editor_->SetReadOnly(false);
|
prompt_editor_->SetReadOnly(false);
|
||||||
prompt_editor_->SetShowWhitespaces(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
|
// Ensure profiles directory exists
|
||||||
EnsureProfilesDirectory();
|
EnsureProfilesDirectory();
|
||||||
}
|
}
|
||||||
@@ -173,34 +179,58 @@ void AgentEditor::DrawDashboard() {
|
|||||||
|
|
||||||
// Compact tabbed interface (combined tabs)
|
// Compact tabbed interface (combined tabs)
|
||||||
if (ImGui::BeginTabBar("AgentEditorTabs", ImGuiTabBarFlags_None)) {
|
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")) {
|
if (ImGui::BeginTabItem(ICON_MD_SMART_TOY " Bot Studio")) {
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
// Three-column layout with prompt editor in center
|
// Use ImGui table for clean 3-column resizable layout
|
||||||
if (ImGui::BeginTable("BotStudioLayout", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) {
|
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("Config", ImGuiTableColumnFlags_WidthFixed, 380.0f);
|
||||||
ImGui::TableSetupColumn("Prompt Editor", ImGuiTableColumnFlags_WidthStretch);
|
ImGui::TableSetupColumn("Editors", ImGuiTableColumnFlags_WidthStretch);
|
||||||
ImGui::TableSetupColumn("Profiles", ImGuiTableColumnFlags_WidthFixed, 320.0f);
|
ImGui::TableSetupColumn("Profiles", ImGuiTableColumnFlags_WidthFixed, 320.0f);
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
|
|
||||||
// LEFT: Configuration
|
// Column 1: Configuration
|
||||||
ImGui::TableSetColumnIndex(0);
|
ImGui::TableNextColumn();
|
||||||
ImGui::BeginChild("ConfigSection", ImVec2(0, 0), false);
|
ImGui::PushID("ConfigColumn");
|
||||||
DrawConfigurationPanel();
|
DrawConfigurationPanel();
|
||||||
ImGui::EndChild();
|
ImGui::PopID();
|
||||||
|
|
||||||
// CENTER: System Prompt Editor
|
// Column 2: Editors (Prompt + Tiles + New)
|
||||||
ImGui::TableSetColumnIndex(1);
|
ImGui::TableNextColumn();
|
||||||
ImGui::BeginChild("PromptSection", ImVec2(0, 0), false);
|
ImGui::PushID("EditorsColumn");
|
||||||
DrawPromptEditorPanel();
|
|
||||||
ImGui::EndChild();
|
|
||||||
|
|
||||||
// RIGHT: Bot Profiles List
|
// Tabbed editors for better organization
|
||||||
ImGui::TableSetColumnIndex(2);
|
if (ImGui::BeginTabBar("EditorTabs", ImGuiTabBarFlags_None)) {
|
||||||
ImGui::BeginChild("ProfilesSection", ImVec2(0, 0), false);
|
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();
|
DrawBotProfilesPanel();
|
||||||
ImGui::EndChild();
|
ImGui::PopID();
|
||||||
|
|
||||||
ImGui::EndTable();
|
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
|
// Bot Profile Management Implementation
|
||||||
absl::Status AgentEditor::SaveBotProfile(const BotProfile& profile) {
|
absl::Status AgentEditor::SaveBotProfile(const BotProfile& profile) {
|
||||||
#if defined(YAZE_WITH_JSON)
|
#if defined(YAZE_WITH_JSON)
|
||||||
|
|||||||
@@ -182,6 +182,8 @@ class AgentEditor : public Editor {
|
|||||||
void DrawBotProfilesPanel();
|
void DrawBotProfilesPanel();
|
||||||
void DrawChatHistoryViewer();
|
void DrawChatHistoryViewer();
|
||||||
void DrawAdvancedMetricsPanel();
|
void DrawAdvancedMetricsPanel();
|
||||||
|
void DrawCommonTilesEditor();
|
||||||
|
void DrawNewPromptCreator();
|
||||||
|
|
||||||
// Setup callbacks
|
// Setup callbacks
|
||||||
void SetupChatWidgetCallbacks();
|
void SetupChatWidgetCallbacks();
|
||||||
@@ -213,8 +215,11 @@ class AgentEditor : public Editor {
|
|||||||
|
|
||||||
// System Prompt Editor
|
// System Prompt Editor
|
||||||
std::unique_ptr<TextEditor> prompt_editor_;
|
std::unique_ptr<TextEditor> prompt_editor_;
|
||||||
|
std::unique_ptr<TextEditor> common_tiles_editor_;
|
||||||
bool prompt_editor_initialized_ = false;
|
bool prompt_editor_initialized_ = false;
|
||||||
|
bool common_tiles_initialized_ = false;
|
||||||
std::string active_prompt_file_ = "system_prompt_v3.txt";
|
std::string active_prompt_file_ = "system_prompt_v3.txt";
|
||||||
|
char new_prompt_name_[128] = {};
|
||||||
|
|
||||||
// Collaboration state
|
// Collaboration state
|
||||||
CollaborationMode current_mode_ = CollaborationMode::kLocal;
|
CollaborationMode current_mode_ = CollaborationMode::kLocal;
|
||||||
|
|||||||
@@ -30,33 +30,29 @@ AgentChatHistoryPopup::AgentChatHistoryPopup() {
|
|||||||
void AgentChatHistoryPopup::Draw() {
|
void AgentChatHistoryPopup::Draw() {
|
||||||
if (!visible_) return;
|
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();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
||||||
ImGui::SetNextWindowSize(ImVec2(drawer_width_, io.DisplaySize.y),
|
ImGui::SetNextWindowSize(ImVec2(drawer_width_, io.DisplaySize.y), ImGuiCond_Always);
|
||||||
ImGuiCond_Always);
|
|
||||||
|
|
||||||
ImGuiWindowFlags flags = ImGuiWindowFlags_NoMove |
|
ImGuiWindowFlags flags = ImGuiWindowFlags_NoMove |
|
||||||
ImGuiWindowFlags_NoResize |
|
ImGuiWindowFlags_NoResize |
|
||||||
ImGuiWindowFlags_NoCollapse |
|
ImGuiWindowFlags_NoCollapse |
|
||||||
ImGuiWindowFlags_NoTitleBar;
|
ImGuiWindowFlags_NoTitleBar;
|
||||||
|
|
||||||
// Theme-matched styling
|
// Use current theme colors
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10, 10));
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10, 10));
|
||||||
|
|
||||||
if (ImGui::Begin("##AgentChatPopup", &visible_, flags)) {
|
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();
|
DrawHeader();
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
// Message list with gradient background
|
// Calculate proper list height
|
||||||
float list_height = io.DisplaySize.y - 280.0f; // Reserve space for input and actions
|
float list_height = ImGui::GetContentRegionAvail().y - 220.0f;
|
||||||
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.05f, 0.05f, 0.08f, 0.95f));
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.05f, 0.05f, 0.08f, 0.95f));
|
||||||
ImGui::BeginChild("MessageList", ImVec2(0, list_height), true, ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
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);
|
ImGui::PushID(index);
|
||||||
|
|
||||||
bool from_user = (msg.sender == cli::agent::ChatMessage::Sender::kUser);
|
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";
|
const char* sender_label = from_user ? ICON_MD_PERSON " You" : ICON_MD_SMART_TOY " Agent";
|
||||||
|
|
||||||
// Message header with sender and timestamp
|
// Message header
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, header_color);
|
ImGui::TextColored(header_color, "%s", sender_label);
|
||||||
ImGui::Text("%s", sender_label);
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, kTimestampColor);
|
ImGui::TextDisabled("%s", absl::FormatTime("%H:%M:%S", msg.timestamp, absl::LocalTimeZone()).c_str());
|
||||||
ImGui::Text("%s", absl::FormatTime("%H:%M:%S", msg.timestamp, absl::LocalTimeZone()).c_str());
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
|
|
||||||
// Message content
|
// Message content
|
||||||
ImGui::Indent(10.0f);
|
ImGui::Indent(10.0f);
|
||||||
@@ -181,10 +179,10 @@ void AgentChatHistoryPopup::DrawHeader() {
|
|||||||
|
|
||||||
ImGui::Dummy(ImVec2(0, 8));
|
ImGui::Dummy(ImVec2(0, 8));
|
||||||
|
|
||||||
// Title with theme colors
|
// Title
|
||||||
ImGui::TextColored(ImGui::GetStyleColorVec4(ImGuiCol_Text), "%s AI Chat", ICON_MD_CHAT);
|
ImGui::Text("%s AI Chat", ICON_MD_CHAT);
|
||||||
|
|
||||||
ImGui::SameLine(ImGui::GetContentRegionAvail().x - 130);
|
ImGui::SameLine(ImGui::GetContentRegionAvail().x - 95);
|
||||||
|
|
||||||
// Compact mode toggle
|
// Compact mode toggle
|
||||||
if (ImGui::SmallButton(compact_mode_ ? ICON_MD_UNFOLD_MORE : ICON_MD_UNFOLD_LESS)) {
|
if (ImGui::SmallButton(compact_mode_ ? ICON_MD_UNFOLD_MORE : ICON_MD_UNFOLD_LESS)) {
|
||||||
@@ -197,18 +195,25 @@ void AgentChatHistoryPopup::DrawHeader() {
|
|||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
// Full chat button
|
// 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 (ImGui::SmallButton(ICON_MD_OPEN_IN_NEW)) {
|
||||||
if (open_chat_callback_) {
|
if (open_chat_callback_) {
|
||||||
open_chat_callback_();
|
open_chat_callback_();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::PopStyleColor(2);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
ImGui::SetTooltip("Open full chat window");
|
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
|
// Message count with badge
|
||||||
int visible_count = 0;
|
int visible_count = 0;
|
||||||
for (const auto& msg : messages_) {
|
for (const auto& msg : messages_) {
|
||||||
@@ -228,9 +233,6 @@ void AgentChatHistoryPopup::DrawHeader() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AgentChatHistoryPopup::DrawQuickActions() {
|
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;
|
float button_width = (ImGui::GetContentRegionAvail().x - 8) / 3.0f;
|
||||||
|
|
||||||
// Multimodal snapshot button
|
// Multimodal snapshot button
|
||||||
@@ -277,20 +279,13 @@ void AgentChatHistoryPopup::DrawQuickActions() {
|
|||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
ImGui::SetTooltip("Clear popup view");
|
ImGui::SetTooltip("Clear popup view");
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::PopStyleColor(2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AgentChatHistoryPopup::DrawInputSection() {
|
void AgentChatHistoryPopup::DrawInputSection() {
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
// Input field with beautiful styling
|
// Input field using theme colors
|
||||||
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);
|
|
||||||
|
|
||||||
bool send_message = false;
|
bool send_message = false;
|
||||||
ImGui::SetNextItemWidth(-1);
|
ImGui::SetNextItemWidth(-1);
|
||||||
if (ImGui::InputTextMultiline("##popup_input", input_buffer_, sizeof(input_buffer_),
|
if (ImGui::InputTextMultiline("##popup_input", input_buffer_, sizeof(input_buffer_),
|
||||||
@@ -305,30 +300,18 @@ void AgentChatHistoryPopup::DrawInputSection() {
|
|||||||
focus_input_ = false;
|
focus_input_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::PopStyleVar();
|
// Send button
|
||||||
ImGui::PopStyleColor(3);
|
|
||||||
|
|
||||||
// Send button with gradient
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(kAccentColor.x, kAccentColor.y, kAccentColor.z, 0.7f));
|
if (ImGui::Button(absl::StrFormat("%s Send", ICON_MD_SEND).c_str(), ImVec2(-1, 30)) || send_message) {
|
||||||
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 (std::strlen(input_buffer_) > 0) {
|
if (std::strlen(input_buffer_) > 0) {
|
||||||
SendMessage(input_buffer_);
|
SendMessage(input_buffer_);
|
||||||
std::memset(input_buffer_, 0, sizeof(input_buffer_));
|
std::memset(input_buffer_, 0, sizeof(input_buffer_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::PopStyleColor(3);
|
|
||||||
|
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
ImGui::SetTooltip("Send message (Enter) • Ctrl+Enter for newline");
|
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) {
|
void AgentChatHistoryPopup::SendMessage(const std::string& message) {
|
||||||
|
|||||||
Reference in New Issue
Block a user