feat: Enhance Agent Chat History Popup with Retro Animations

- Introduced retro-style animations including pulsing borders, scanline effects, and glitch animations to the Agent Chat History Popup, enhancing visual appeal.
- Updated message display with terminal-inspired styling, including color-coded sender labels and retro separators.
- Improved header design with a pulsing glow effect and animated status indicators for unread messages, enriching user interaction.
- Refactored drawing methods to accommodate new visual effects while maintaining existing functionality.
This commit is contained in:
scawful
2025-10-06 01:33:08 -04:00
parent 7ba8d5b443
commit 8688f0c502
7 changed files with 368 additions and 71 deletions

View File

@@ -23,9 +23,15 @@ void AgentChatHistoryPopup::Draw() {
const auto& theme = AgentUI::GetTheme();
// Set drawer position on the LEFT side (full height)
// Animate retro effects
ImGuiIO& io = ImGui::GetIO();
pulse_animation_ += io.DeltaTime * 2.0f;
scanline_offset_ += io.DeltaTime * 0.3f;
if (scanline_offset_ > 1.0f) scanline_offset_ -= 1.0f;
glitch_animation_ += io.DeltaTime * 5.0f;
blink_counter_ = static_cast<int>(pulse_animation_ * 2.0f) % 2;
// Set drawer position on the LEFT side (full height)
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(drawer_width_, io.DisplaySize.y), ImGuiCond_Always);
@@ -34,10 +40,19 @@ void AgentChatHistoryPopup::Draw() {
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoTitleBar;
// Use current theme colors
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
// Use current theme colors with slight glow
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 2.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10, 10));
// Pulsing border color
float border_pulse = 0.7f + 0.3f * std::sin(pulse_animation_ * 0.5f);
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(
theme.provider_ollama.x * border_pulse,
theme.provider_ollama.y * border_pulse,
theme.provider_ollama.z * border_pulse + 0.2f,
0.8f
));
if (ImGui::Begin("##AgentChatPopup", &visible_, flags)) {
DrawHeader();
@@ -47,8 +62,30 @@ void AgentChatHistoryPopup::Draw() {
// Calculate proper list height
float list_height = ImGui::GetContentRegionAvail().y - 220.0f;
ImGui::PushStyleColor(ImGuiCol_ChildBg, theme.code_bg_color);
// Dark terminal background
ImVec4 terminal_bg = theme.code_bg_color;
terminal_bg.x *= 0.9f;
terminal_bg.y *= 0.9f;
terminal_bg.z *= 0.95f;
ImGui::PushStyleColor(ImGuiCol_ChildBg, terminal_bg);
ImGui::BeginChild("MessageList", ImVec2(0, list_height), true, ImGuiWindowFlags_AlwaysVerticalScrollbar);
// Draw scanline effect
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 win_pos = ImGui::GetWindowPos();
ImVec2 win_size = ImGui::GetWindowSize();
for (float y = 0; y < win_size.y; y += 3.0f) {
float offset_y = y + scanline_offset_ * 3.0f;
if (offset_y < win_size.y) {
draw_list->AddLine(
ImVec2(win_pos.x, win_pos.y + offset_y),
ImVec2(win_pos.x + win_size.x, win_pos.y + offset_y),
IM_COL32(0, 0, 0, 15));
}
}
DrawMessageList();
if (needs_scroll_) {
@@ -72,6 +109,7 @@ void AgentChatHistoryPopup::Draw() {
}
ImGui::End();
ImGui::PopStyleColor(); // Border color
ImGui::PopStyleVar(2);
}
@@ -106,100 +144,140 @@ void AgentChatHistoryPopup::DrawMessage(const cli::agent::ChatMessage& msg, int
bool from_user = (msg.sender == cli::agent::ChatMessage::Sender::kUser);
// Use theme colors with slight tint
ImVec4 text_color = ImGui::GetStyleColorVec4(ImGuiCol_Text);
// Retro terminal colors
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
? ImVec4(1.0f, 0.85f, 0.0f, 1.0f) // Amber/Gold for user
: ImVec4(0.0f, 1.0f, 0.7f, 1.0f); // Cyan/Green for agent
const char* sender_label = from_user ? ICON_MD_PERSON " You" : ICON_MD_SMART_TOY " Agent";
const char* sender_label = from_user ? "> USER:" : "> AGENT:";
// Message header
// Message header with terminal prefix
ImGui::TextColored(header_color, "%s", sender_label);
ImGui::SameLine();
ImGui::TextDisabled("%s", absl::FormatTime("%H:%M:%S", msg.timestamp, absl::LocalTimeZone()).c_str());
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
"[%s]", absl::FormatTime("%H:%M:%S", msg.timestamp, absl::LocalTimeZone()).c_str());
// Message content
ImGui::Indent(10.0f);
// Message content with terminal styling
ImGui::Indent(15.0f);
if (msg.table_data.has_value()) {
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.9f, 1.0f), ICON_MD_TABLE_CHART " [Table Data]");
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.9f, 1.0f),
" %s [Table Data]", ICON_MD_TABLE_CHART);
} else if (msg.json_pretty.has_value()) {
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.9f, 1.0f), ICON_MD_DATA_OBJECT " [Structured Response]");
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.9f, 1.0f),
" %s [Structured Response]", ICON_MD_DATA_OBJECT);
} else {
// Truncate long messages
// Truncate long messages with ellipsis
std::string content = msg.message;
if (content.length() > 200) {
content = content.substr(0, 197) + "...";
}
ImGui::TextWrapped("%s", content.c_str());
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.85f, 0.85f, 0.85f, 1.0f));
ImGui::TextWrapped(" %s", content.c_str());
ImGui::PopStyleColor();
}
// Show proposal indicator if present
// Show proposal indicator with pulse
if (msg.proposal.has_value()) {
ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.4f, 1.0f),
ICON_MD_PREVIEW " Proposal: %s", msg.proposal->id.c_str());
float proposal_pulse = 0.7f + 0.3f * std::sin(pulse_animation_ * 2.0f);
ImGui::TextColored(ImVec4(0.2f, proposal_pulse, 0.4f, 1.0f),
" %s Proposal: [%s]", ICON_MD_PREVIEW, msg.proposal->id.c_str());
}
ImGui::Unindent(10.0f);
ImGui::Unindent(15.0f);
ImGui::Spacing();
ImGui::Separator();
// Retro separator line
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 line_start = ImGui::GetCursorScreenPos();
float line_width = ImGui::GetContentRegionAvail().x;
draw_list->AddLine(
line_start,
ImVec2(line_start.x + line_width, line_start.y),
IM_COL32(60, 60, 70, 100),
1.0f
);
ImGui::Dummy(ImVec2(0, 2));
ImGui::PopID();
}
void AgentChatHistoryPopup::DrawHeader() {
// Theme-matched header with subtle gradient
const auto& theme = AgentUI::GetTheme();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 header_start = ImGui::GetCursorScreenPos();
ImVec2 header_size(ImGui::GetContentRegionAvail().x, 55);
// Subtle gradient matching theme
ImU32 color_top = ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_WindowBg));
ImU32 color_bottom = ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_ChildBg));
// Retro gradient with pulse
float pulse = 0.5f + 0.5f * std::sin(pulse_animation_);
ImVec4 bg_top = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
ImVec4 bg_bottom = ImGui::GetStyleColorVec4(ImGuiCol_ChildBg);
bg_top.x += 0.1f * pulse;
bg_top.y += 0.1f * pulse;
bg_top.z += 0.15f * pulse;
ImU32 color_top = ImGui::GetColorU32(bg_top);
ImU32 color_bottom = ImGui::GetColorU32(bg_bottom);
draw_list->AddRectFilledMultiColor(
header_start,
ImVec2(header_start.x + header_size.x, header_start.y + header_size.y),
color_top, color_top, color_bottom, color_bottom);
// Thin accent line (no pulse - matches theme better)
ImU32 accent_color = ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_Separator));
// Pulsing accent line with glow
float line_pulse = 0.6f + 0.4f * std::sin(pulse_animation_ * 0.7f);
ImU32 accent_color = IM_COL32(
static_cast<int>(theme.provider_ollama.x * 255 * line_pulse),
static_cast<int>(theme.provider_ollama.y * 255 * line_pulse),
static_cast<int>(theme.provider_ollama.z * 255 * line_pulse + 50),
200
);
draw_list->AddLine(
ImVec2(header_start.x, header_start.y + header_size.y),
ImVec2(header_start.x + header_size.x, header_start.y + header_size.y),
accent_color, 1.5f);
accent_color, 2.0f);
ImGui::Dummy(ImVec2(0, 8));
// Title and provider dropdown (like connection header)
ImGui::Text(ICON_MD_CHAT);
ImGui::SameLine();
// Title with pulsing glow
ImVec4 title_color = ImVec4(
0.4f + 0.3f * pulse,
0.8f + 0.2f * pulse,
1.0f,
1.0f
);
ImGui::PushStyleColor(ImGuiCol_Text, title_color);
ImGui::Text("%s CHAT HISTORY", ICON_MD_CHAT);
ImGui::PopStyleColor();
// Model dropdown (compact)
ImGui::SetNextItemWidth(120);
static int provider_idx = 0;
const char* providers[] = {"Mock", "Ollama", "Gemini"};
ImGui::Combo("##popup_provider", &provider_idx, providers, 3);
ImGui::SameLine();
ImGui::TextDisabled("[v0.4.x]");
// Buttons properly spaced from right edge
ImGui::SameLine(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 75.0f);
// Compact mode toggle
// Compact mode toggle with pulse
if (blink_counter_ == 0 && compact_mode_) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.4f, 0.6f, 0.7f));
}
if (ImGui::SmallButton(compact_mode_ ? ICON_MD_UNFOLD_MORE : ICON_MD_UNFOLD_LESS)) {
compact_mode_ = !compact_mode_;
}
if (blink_counter_ == 0 && compact_mode_) {
ImGui::PopStyleColor();
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(compact_mode_ ? "Expand view" : "Compact view");
}
ImGui::SameLine();
// Full chat button (closes popup when opened)
// Full chat button
if (ImGui::SmallButton(ICON_MD_OPEN_IN_NEW)) {
if (open_chat_callback_) {
open_chat_callback_();
visible_ = false; // Close popup when opening main chat
visible_ = false;
}
}
if (ImGui::IsItemHovered()) {
@@ -216,7 +294,7 @@ void AgentChatHistoryPopup::DrawHeader() {
ImGui::SetTooltip("Close (Ctrl+H)");
}
// Message count with badge
// Message count with retro styling
int visible_count = 0;
for (const auto& msg : messages_) {
if (msg.is_internal) continue;
@@ -228,8 +306,16 @@ void AgentChatHistoryPopup::DrawHeader() {
}
ImGui::Spacing();
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 0.9f),
"%d message%s", visible_count, visible_count == 1 ? "" : "s");
ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f),
"> MESSAGES: [%d]", visible_count);
// Animated status indicator
if (unread_count_ > 0) {
ImGui::SameLine();
float unread_pulse = 0.5f + 0.5f * std::sin(pulse_animation_ * 3.0f);
ImGui::TextColored(ImVec4(1.0f, unread_pulse * 0.5f, 0.0f, 1.0f),
"%s %d NEW", ICON_MD_NOTIFICATION_IMPORTANT, unread_count_);
}
ImGui::Dummy(ImVec2(0, 5));
}

View File

@@ -112,6 +112,12 @@ class AgentChatHistoryPopup {
float header_pulse_ = 0.0f;
int unread_count_ = 0;
// Retro hacker aesthetic animations
float pulse_animation_ = 0.0f;
float scanline_offset_ = 0.0f;
float glitch_animation_ = 0.0f;
int blink_counter_ = 0;
// Dependencies
ToastManager* toast_manager_ = nullptr;
OpenChatCallback open_chat_callback_;

View File

@@ -1491,29 +1491,91 @@ void AgentChatWidget::RenderAutomationPanel() {
const auto& theme = AgentUI::GetTheme();
ImGui::PushID("AutomationPanel");
AgentUI::PushPanelStyle();
if (ImGui::BeginChild("Automation_Panel", ImVec2(0, 180), true)) {
AgentUI::RenderSectionHeader(ICON_MD_SMART_TOY, "GUI Automation",
theme.provider_ollama);
// Auto-poll for status updates
PollAutomationStatus();
// Connection status
bool connected = automation_state_.harness_connected;
ImVec4 status_color = connected ? theme.status_success : theme.status_warning;
const char* status_text = connected ? "Connected" : "Disconnected";
// Animate pulse and scanlines for retro effect
automation_state_.pulse_animation += ImGui::GetIO().DeltaTime * 2.0f;
automation_state_.scanline_offset += ImGui::GetIO().DeltaTime * 0.5f;
if (automation_state_.scanline_offset > 1.0f) {
automation_state_.scanline_offset -= 1.0f;
}
AgentUI::PushPanelStyle();
if (ImGui::BeginChild("Automation_Panel", ImVec2(0, 240), true)) {
// === HEADER WITH RETRO GLITCH EFFECT ===
float pulse = 0.5f + 0.5f * std::sin(automation_state_.pulse_animation);
ImVec4 header_glow = ImVec4(
theme.provider_ollama.x + 0.3f * pulse,
theme.provider_ollama.y + 0.2f * pulse,
theme.provider_ollama.z + 0.4f * pulse,
1.0f
);
ImGui::PushStyleColor(ImGuiCol_Text, header_glow);
ImGui::TextWrapped("%s %s", ICON_MD_SMART_TOY, "GUI AUTOMATION");
ImGui::PopStyleColor();
ImGui::TextColored(status_color, "%s %s",
connected ? ICON_MD_CHECK_CIRCLE : ICON_MD_WARNING,
status_text);
ImGui::SameLine();
ImGui::TextDisabled("[v0.4.x]");
// === CONNECTION STATUS WITH VISUAL EFFECTS ===
bool connected = automation_state_.harness_connected;
ImVec4 status_color;
const char* status_text;
const char* status_icon;
if (connected) {
// Pulsing green for connected
float green_pulse = 0.7f + 0.3f * std::sin(automation_state_.pulse_animation * 0.5f);
status_color = ImVec4(0.1f, green_pulse, 0.3f, 1.0f);
status_text = "ONLINE";
status_icon = ICON_MD_CHECK_CIRCLE;
} else {
// Pulsing red for disconnected
float red_pulse = 0.6f + 0.4f * std::sin(automation_state_.pulse_animation * 1.5f);
status_color = ImVec4(red_pulse, 0.2f, 0.2f, 1.0f);
status_text = "OFFLINE";
status_icon = ICON_MD_ERROR;
}
ImGui::Separator();
ImGui::TextColored(status_color, "%s %s", status_icon, status_text);
ImGui::SameLine();
ImGui::TextDisabled("| %s", automation_state_.grpc_server_address.c_str());
// === CONTROL BAR ===
ImGui::Spacing();
// Refresh button with pulse effect when auto-refresh is on
bool auto_ref_pulse = automation_state_.auto_refresh_enabled &&
(static_cast<int>(automation_state_.pulse_animation * 2.0f) % 2 == 0);
if (auto_ref_pulse) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.5f, 0.7f, 0.8f));
}
if (ImGui::SmallButton(ICON_MD_REFRESH " Refresh")) {
PollAutomationStatus();
if (automation_callbacks_.show_active_tests) {
automation_callbacks_.show_active_tests();
}
}
if (auto_ref_pulse) {
ImGui::PopStyleColor();
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Refresh automation status");
ImGui::SetTooltip("Refresh automation status\nAuto-refresh: %s (%.1fs)",
automation_state_.auto_refresh_enabled ? "ON" : "OFF",
automation_state_.refresh_interval_seconds);
}
// Auto-refresh toggle
ImGui::SameLine();
ImGui::Checkbox("##auto_refresh", &automation_state_.auto_refresh_enabled);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Auto-refresh connection status");
}
// Quick action buttons
@@ -1523,6 +1585,9 @@ void AgentChatWidget::RenderAutomationPanel() {
automation_callbacks_.open_harness_dashboard();
}
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Open automation dashboard");
}
ImGui::SameLine();
if (ImGui::SmallButton(ICON_MD_REPLAY " Replay")) {
@@ -1530,33 +1595,78 @@ void AgentChatWidget::RenderAutomationPanel() {
automation_callbacks_.replay_last_plan();
}
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Replay last automation plan");
}
// Recent automation actions
// === SETTINGS ROW ===
ImGui::Spacing();
ImGui::SetNextItemWidth(80.0f);
ImGui::SliderFloat("##refresh_interval", &automation_state_.refresh_interval_seconds,
0.5f, 10.0f, "%.1fs");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Auto-refresh interval");
}
// === RECENT AUTOMATION ACTIONS WITH SCROLLING ===
ImGui::Spacing();
ImGui::Separator();
ImGui::Text(ICON_MD_LIST " Recent Actions:");
// Header with retro styling
ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f), "%s RECENT ACTIONS", ICON_MD_LIST);
ImGui::SameLine();
ImGui::TextDisabled("[%zu]", automation_state_.recent_tests.size());
if (automation_state_.recent_tests.empty()) {
ImGui::TextDisabled(" No recent actions");
ImGui::Spacing();
ImGui::TextDisabled(" > No recent actions");
ImGui::TextDisabled(" > Waiting for automation tasks...");
// Add animated dots
int dots = static_cast<int>(automation_state_.pulse_animation) % 4;
std::string dot_string(dots, '.');
ImGui::TextDisabled(" > %s", dot_string.c_str());
} else {
ImGui::BeginChild("ActionQueue", ImVec2(0, 80), false);
// Scrollable action list with retro styling
ImGui::BeginChild("ActionQueue", ImVec2(0, 100), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar);
// Add scanline effect (visual only)
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 win_pos = ImGui::GetWindowPos();
ImVec2 win_size = ImGui::GetWindowSize();
// Draw scanlines
for (float y = 0; y < win_size.y; y += 4.0f) {
float offset_y = y + automation_state_.scanline_offset * 4.0f;
if (offset_y < win_size.y) {
draw_list->AddLine(
ImVec2(win_pos.x, win_pos.y + offset_y),
ImVec2(win_pos.x + win_size.x, win_pos.y + offset_y),
IM_COL32(0, 0, 0, 20));
}
}
for (const auto& test : automation_state_.recent_tests) {
ImGui::PushID(test.test_id.c_str());
// Status icon
// Status icon with animation for running tests
ImVec4 action_color;
const char* status_icon;
bool is_running = false;
if (test.status == "success" || test.status == "completed") {
if (test.status == "success" || test.status == "completed" || test.status == "passed") {
action_color = theme.status_success;
status_icon = ICON_MD_CHECK_CIRCLE;
} else if (test.status == "running" || test.status == "in_progress") {
action_color = theme.provider_ollama;
is_running = true;
float running_pulse = 0.5f + 0.5f * std::sin(automation_state_.pulse_animation * 3.0f);
action_color = ImVec4(
theme.provider_ollama.x * running_pulse,
theme.provider_ollama.y * (0.8f + 0.2f * running_pulse),
theme.provider_ollama.z * running_pulse,
1.0f
);
status_icon = ICON_MD_PENDING;
} else if (test.status == "failed" || test.status == "error") {
action_color = theme.status_error;
@@ -1566,29 +1676,35 @@ void AgentChatWidget::RenderAutomationPanel() {
status_icon = ICON_MD_HELP;
}
// Icon with pulse
ImGui::TextColored(action_color, "%s", status_icon);
ImGui::SameLine();
// Action name
ImGui::Text("%s", test.name.c_str());
// Action name with monospace font
ImGui::Text("> %s", test.name.c_str());
// Timestamp
if (test.updated_at != absl::InfinitePast()) {
ImGui::SameLine();
auto elapsed = absl::Now() - test.updated_at;
if (elapsed < absl::Seconds(60)) {
ImGui::TextDisabled("(%ds ago)", static_cast<int>(absl::ToInt64Seconds(elapsed)));
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
"[%ds]", static_cast<int>(absl::ToInt64Seconds(elapsed)));
} else if (elapsed < absl::Minutes(60)) {
ImGui::TextDisabled("(%dm ago)", static_cast<int>(absl::ToInt64Minutes(elapsed)));
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
"[%dm]", static_cast<int>(absl::ToInt64Minutes(elapsed)));
} else {
ImGui::TextDisabled("(%dh ago)", static_cast<int>(absl::ToInt64Hours(elapsed)));
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
"[%dh]", static_cast<int>(absl::ToInt64Hours(elapsed)));
}
}
// Message (if any)
// Message (if any) with indentation
if (!test.message.empty()) {
ImGui::Indent(20.0f);
ImGui::TextWrapped(ICON_MD_MESSAGE " %s", test.message.c_str());
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f));
ImGui::TextWrapped(" %s %s", ICON_MD_MESSAGE, test.message.c_str());
ImGui::PopStyleColor();
ImGui::Unindent(20.0f);
}
@@ -2751,6 +2867,58 @@ void AgentChatWidget::SetLastPlanSummary(const std::string& /* summary */) {
}
}
void AgentChatWidget::PollAutomationStatus() {
// Check if we should poll based on interval and auto-refresh setting
if (!automation_state_.auto_refresh_enabled) {
return;
}
absl::Time now = absl::Now();
absl::Duration elapsed = now - automation_state_.last_poll;
if (elapsed < absl::Seconds(automation_state_.refresh_interval_seconds)) {
return;
}
// Update last poll time
automation_state_.last_poll = now;
// Check connection status
bool was_connected = automation_state_.harness_connected;
automation_state_.harness_connected = CheckHarnessConnection();
// Notify on status change
if (was_connected != automation_state_.harness_connected && toast_manager_) {
if (automation_state_.harness_connected) {
toast_manager_->Show(ICON_MD_CHECK_CIRCLE " Automation harness connected",
ToastType::kSuccess, 2.0f);
} else {
toast_manager_->Show(ICON_MD_WARNING " Automation harness disconnected",
ToastType::kWarning, 2.0f);
}
}
}
bool AgentChatWidget::CheckHarnessConnection() {
#if defined(YAZE_WITH_GRPC)
try {
// Attempt to get harness summaries from TestManager
// If this succeeds, the harness infrastructure is working
auto summaries = test::TestManager::Get().ListHarnessTestSummaries();
// If we get here, the test manager is operational
// In a real implementation, you might want to ping the gRPC server
automation_state_.connection_attempts = 0;
return true;
} catch (const std::exception& e) {
automation_state_.connection_attempts++;
return false;
}
#else
return false;
#endif
}
void AgentChatWidget::SyncHistoryToPopup() {
if (!chat_history_popup_) {
return;

View File

@@ -125,6 +125,10 @@ class AgentChatWidget {
void UpdateHarnessTelemetry(const AutomationTelemetry& telemetry);
void SetLastPlanSummary(const std::string& summary);
// Automation status polling
void PollAutomationStatus();
bool CheckHarnessConnection();
void SetZ3EDCommandCallbacks(const Z3EDCommandCallbacks& callbacks) {
z3ed_callbacks_ = callbacks;
@@ -194,6 +198,13 @@ public:
std::vector<AutomationTelemetry> recent_tests;
bool harness_connected = false;
absl::Time last_poll = absl::InfinitePast();
bool auto_refresh_enabled = true;
float refresh_interval_seconds = 2.0f;
float pulse_animation = 0.0f;
float scanline_offset = 0.0f;
int connection_attempts = 0;
absl::Time last_connection_attempt = absl::InfinitePast();
std::string grpc_server_address = "localhost:50052";
};
// Agent Configuration State

View File

@@ -140,8 +140,25 @@ void AgentEditor::DrawDashboard() {
if (!active_)
return;
// Animate retro effects
ImGuiIO& io = ImGui::GetIO();
pulse_animation_ += io.DeltaTime * 2.0f;
scanline_offset_ += io.DeltaTime * 0.4f;
if (scanline_offset_ > 1.0f) scanline_offset_ -= 1.0f;
glitch_timer_ += io.DeltaTime * 5.0f;
blink_counter_ = static_cast<int>(pulse_animation_ * 2.0f) % 2;
// Pulsing glow for window
float pulse = 0.5f + 0.5f * std::sin(pulse_animation_);
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, ImVec4(
0.1f + 0.1f * pulse,
0.2f + 0.15f * pulse,
0.3f + 0.2f * pulse,
1.0f
));
ImGui::SetNextWindowSize(ImVec2(1200, 800), ImGuiCond_FirstUseEver);
ImGui::Begin(ICON_MD_SMART_TOY " AI Agent Platform & Bot Creator", &active_,
ImGui::Begin(ICON_MD_SMART_TOY " AI AGENT PLATFORM [v0.4.x]", &active_,
ImGuiWindowFlags_MenuBar);
// Menu bar

View File

@@ -103,6 +103,12 @@ class AgentEditor : public Editor {
int max_tool_iterations = 4;
};
// Retro hacker animation state
float pulse_animation_ = 0.0f;
float scanline_offset_ = 0.0f;
float glitch_timer_ = 0.0f;
int blink_counter_ = 0;
AgentConfig GetCurrentConfig() const;
void ApplyConfig(const AgentConfig& config);