7#include "absl/strings/str_format.h"
8#include "absl/time/clock.h"
9#include "absl/time/time.h"
14#include "imgui/imgui.h"
15#include "yaze_config.h"
21 const AutomationCallbacks& callbacks) {
23 auto& state = context->automation_state();
25 ImGui::PushID(
"AutomationPanel");
28 if (callbacks.poll_status) {
29 callbacks.poll_status();
33 state.pulse_animation += ImGui::GetIO().DeltaTime * 2.0f;
34 state.scanline_offset += ImGui::GetIO().DeltaTime * 0.5f;
35 if (state.scanline_offset > 1.0f) {
36 state.scanline_offset -= 1.0f;
40 if (ImGui::BeginChild(
"Automation_Panel", ImVec2(0, 240),
true)) {
42 float pulse = 0.5f + 0.5f * std::sin(state.pulse_animation);
43 ImVec4 header_glow = ImVec4(theme.provider_ollama.x + 0.3f * pulse,
44 theme.provider_ollama.y + 0.2f * pulse,
45 theme.provider_ollama.z + 0.4f * pulse, 1.0f);
48 gui::StyleColorGuard glow_guard(ImGuiCol_Text, header_glow);
56 bool connected = state.harness_connected;
58 const char* status_text;
59 const char* status_icon;
63 float green_pulse = 0.7f + 0.3f * std::sin(state.pulse_animation * 0.5f);
66 ImVec4(success.x, success.y * green_pulse, success.z, 1.0f);
67 status_text =
"ONLINE";
71 float red_pulse = 0.6f + 0.4f * std::sin(state.pulse_animation * 1.5f);
73 status_color = ImVec4(error.x * red_pulse, error.y, error.z, 1.0f);
74 status_text =
"OFFLINE";
79 ImGui::TextColored(status_color,
"%s %s", status_icon, status_text);
81 ImGui::TextDisabled(
"| %s", state.grpc_server_address.c_str());
88 state.auto_refresh_enabled &&
89 (
static_cast<int>(state.pulse_animation * 2.0f) % 2 == 0);
91 std::optional<gui::StyleColorGuard> pulse_guard;
94 pulse_guard.emplace(ImGuiCol_Button,
95 ImVec4(info.x, info.y, info.z, 0.8f));
99 if (callbacks.poll_status) {
100 callbacks.poll_status();
102 if (callbacks.show_active_tests) {
103 callbacks.show_active_tests();
108 if (ImGui::IsItemHovered()) {
109 ImGui::SetTooltip(
"Refresh automation status\nAuto-refresh: %s (%.1fs)",
110 state.auto_refresh_enabled ?
"ON" :
"OFF",
111 state.refresh_interval_seconds);
116 ImGui::Checkbox(
"##auto_refresh", &state.auto_refresh_enabled);
117 if (ImGui::IsItemHovered()) {
118 ImGui::SetTooltip(
"Auto-refresh connection status");
124 if (callbacks.open_harness_dashboard) {
125 callbacks.open_harness_dashboard();
128 if (ImGui::IsItemHovered()) {
129 ImGui::SetTooltip(
"Open automation dashboard");
134 if (callbacks.replay_last_plan) {
135 callbacks.replay_last_plan();
138 if (ImGui::IsItemHovered()) {
139 ImGui::SetTooltip(
"Replay last automation plan");
144 ImGui::SetNextItemWidth(80.0f);
145 ImGui::SliderFloat(
"##refresh_interval", &state.refresh_interval_seconds,
146 0.5f, 10.0f,
"%.1fs");
147 if (ImGui::IsItemHovered()) {
148 ImGui::SetTooltip(
"Auto-refresh interval");
153 ImGui::TextDisabled(
"Automation Hooks");
154 ImGui::Checkbox(
"Auto-run harness plan", &state.auto_run_plan);
155 ImGui::Checkbox(
"Auto-sync ROM context", &state.auto_sync_rom);
156 ImGui::Checkbox(
"Auto-focus proposal drawer", &state.auto_focus_proposals);
165 ImGui::TextDisabled(
"[%zu]", state.recent_tests.size());
167 if (state.recent_tests.empty()) {
169 ImGui::TextDisabled(
" > No recent actions");
170 ImGui::TextDisabled(
" > Waiting for automation tasks...");
173 int dots =
static_cast<int>(state.pulse_animation) % 4;
174 std::string dot_string(dots,
'.');
175 ImGui::TextDisabled(
" > %s", dot_string.c_str());
178 ImGui::BeginChild(
"ActionQueue", ImVec2(0, 100),
true,
179 ImGuiWindowFlags_AlwaysVerticalScrollbar);
182 ImDrawList* draw_list = ImGui::GetWindowDrawList();
183 ImVec2 win_pos = ImGui::GetWindowPos();
184 ImVec2 win_size = ImGui::GetWindowSize();
187 for (
float y = 0; y < win_size.y; y += 4.0f) {
188 float offset_y = y + state.scanline_offset * 4.0f;
189 if (offset_y < win_size.y) {
191 ImVec2(win_pos.x, win_pos.y + offset_y),
192 ImVec2(win_pos.x + win_size.x, win_pos.y + offset_y),
193 IM_COL32(0, 0, 0, 20));
197 for (
const auto& test : state.recent_tests) {
198 ImGui::PushID(test.test_id.c_str());
202 const char* status_icon;
204 if (test.status ==
"success" || test.status ==
"completed" ||
205 test.status ==
"passed") {
206 action_color = theme.status_success;
208 }
else if (test.status ==
"running" || test.status ==
"in_progress") {
209 float running_pulse =
210 0.5f + 0.5f * std::sin(state.pulse_animation * 3.0f);
212 ImVec4(theme.provider_ollama.x * running_pulse,
213 theme.provider_ollama.y * (0.8f + 0.2f * running_pulse),
214 theme.provider_ollama.z * running_pulse, 1.0f);
216 }
else if (test.status ==
"failed" || test.status ==
"error") {
217 action_color = theme.status_error;
220 action_color = theme.text_secondary_color;
225 ImGui::TextColored(action_color,
"%s", status_icon);
229 ImGui::Text(
"> %s", test.name.c_str());
232 if (test.updated_at != absl::InfinitePast()) {
234 auto elapsed = absl::Now() - test.updated_at;
235 if (elapsed < absl::Seconds(60)) {
237 static_cast<int>(absl::ToInt64Seconds(elapsed)));
238 }
else if (elapsed < absl::Minutes(60)) {
240 static_cast<int>(absl::ToInt64Minutes(elapsed)));
243 static_cast<int>(absl::ToInt64Hours(elapsed)));
248 if (!test.message.empty()) {
249 ImGui::Indent(20.0f);
251 gui::StyleColorGuard msg_color(ImGuiCol_Text,
254 test.message.c_str());
256 ImGui::Unindent(20.0f);
void Draw(AgentUIContext *context, const AutomationCallbacks &callbacks)
#define YAZE_VERSION_STRING
#define ICON_MD_CHECK_CIRCLE
#define ICON_MD_DASHBOARD
#define ICON_MD_SMART_TOY
const AgentUITheme & GetTheme()
ImVec4 GetDisabledColor()